<commit_before>#![allow(missing_copy_implementations)]
#![allow(missing_debug_implementations)]
#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]

use crate::builder::Command;
use crate::builder::StyledStr;
#[cfg(feature = "error-context")]
use crate::error::ContextKind;
#[cfg(feature = "error-context")]
use crate::error::ContextValue;
use crate::error::ErrorKind;
use crate::output::TAB;

/// Defines how to format an error for displaying to the user
pub trait ErrorFormatter: Sized {
    /// Stylize the error for the terminal
    fn format_error(error: &crate::error::Error<Self>) -> StyledStr;
}

/// Report [`ErrorKind`]
///
/// No context is included.
///
/// **NOTE:** Consider removing the [`error-context`][crate::_features] default feature if using this to remove all
/// overhead for [`RichFormatter`].
#[non_exhaustive]
pub struct KindFormatter;

impl ErrorFormatter for KindFormatter {
    fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
        let mut styled = StyledStr::new();
        start_error(&mut styled);
        if let Some(msg) = error.kind().as_str() {
            styled.none(msg.to_owned());
        } else if let Some(source) = error.inner.source.as_ref() {
            styled.none(source.to_string());
        } else {
            styled.none("Unknown cause");
        }
        styled.none("\n");
        styled
    }
}

/// Richly formatted error context
#[non_exhaustive]
#[cfg(feature = "error-context")]
pub struct RichFormatter;

#[cfg(feature = "error-context")]
impl ErrorFormatter for RichFormatter {
    fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
        let mut styled = StyledStr::new();
        start_error(&mut styled);

        if !write_dynamic_context(error, &mut styled) {
            if let Some(msg) = error.kind().as_str() {
                styled.none(msg.to_owned());
            } else if let Some(source) = error.inner.source.as_ref() {
                styled.none(source.to_string());
            } else {
                styled.none("Unknown cause");
            }
        }

        let usage = error.get(ContextKind::Usage);
        if let Some(ContextValue::StyledStr(usage)) = usage {
            put_usage(&mut styled, usage.clone());
        }

        try_help(&mut styled, error.inner.help_flag);

        styled
    }
}

fn start_error(styled: &mut StyledStr) {
    styled.error("error:");
    styled.none(" ");
}

#[must_use]
#[cfg(feature = "error-context")]
fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) -> bool {
    match error.kind() {
        ErrorKind::ArgumentConflict => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let prior_arg = error.get(ContextKind::PriorArg);
            if let (Some(ContextValue::String(invalid_arg)), Some(prior_arg)) =
                (invalid_arg, prior_arg)
            {
                if ContextValue::String(invalid_arg.clone()) == *prior_arg {
                    styled.none("The argument '");
                    styled.warning(invalid_arg);
                    styled.none("' was provided more than once, but cannot be used multiple times");
                } else {
                    styled.none("The argument '");
                    styled.warning(invalid_arg);
                    styled.none("' cannot be used with");

                    match prior_arg {
                        ContextValue::Strings(values) => {
                            styled.none(":");
                            for v in values {
                                styled.none("\n");
                                styled.none(TAB);
                                styled.warning(&**v);
                            }
                        }
                        ContextValue::String(value) => {
                            styled.none(" '");
                            styled.warning(value);
                            styled.none("'");
                        }
                        _ => {
                            styled.none(" one or more of the other specified arguments");
                        }
                    }
                }
                true
            } else {
                false
            }
        }
        ErrorKind::NoEquals => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
                styled.none("Equal sign is needed when assigning values to '");
                styled.warning(invalid_arg);
                styled.none("'.");
                true
            } else {
                false
            }
        }
        ErrorKind::InvalidValue => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let invalid_value = error.get(ContextKind::InvalidValue);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::String(invalid_value)),
            ) = (invalid_arg, invalid_value)
            {
                if invalid_value.is_empty() {
                    styled.none("The argument '");
                    styled.warning(invalid_arg);
                    styled.none("' requires a value but none was supplied");
                } else {
                    styled.none("'");
                    styled.none(invalid_value);
                    styled.none("' isn't a valid value for '");
                    styled.warning(invalid_arg);
                    styled.none("'");
                }

                let possible_values = error.get(ContextKind::ValidValue);
                if let Some(ContextValue::Strings(possible_values)) = possible_values {
                    if !possible_values.is_empty() {
                        styled.none("\n");
                        styled.none(TAB);
                        styled.none("[possible values: ");
                        if let Some((last, elements)) = possible_values.split_last() {
                            for v in elements {
                                styled.good(escape(v));
                                styled.none(", ");
                            }
                            styled.good(escape(last));
                        }
                        styled.none("]");
                    }
                }

                if let Some(valid) = error.get(ContextKind::SuggestedValue) {
                    styled.none("\n\n");
                    did_you_mean(styled, valid);
                }
                true
            } else {
                false
            }
        }
        ErrorKind::InvalidSubcommand => {
            let invalid_sub = error.get(ContextKind::InvalidSubcommand);
            if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
                styled.none("The subcommand '");
                styled.warning(invalid_sub);
                styled.none("' wasn't recognized");

                if let Some(valid) = error.get(ContextKind::SuggestedSubcommand) {
                    styled.none("\n\n");
                    did_you_mean(styled, valid);
                }

                let suggestion = error.get(ContextKind::SuggestedCommand);
                if let Some(ContextValue::String(suggestion)) = suggestion {
                    styled.none("\n\n");
                    styled.none(TAB);
                    styled.none(
                        "If you believe you received this message in error, try re-running with '",
                    );
                    styled.good(suggestion);
                    styled.none("'");
                }
                true
            } else {
                false
            }
        }
        ErrorKind::MissingRequiredArgument => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg {
                styled.none("The following required arguments were not provided:");
                for v in invalid_arg {
                    styled.none("\n");
                    styled.none(TAB);
                    styled.good(&**v);
                }
                true
            } else {
                false
            }
        }
        ErrorKind::MissingSubcommand => {
            let invalid_sub = error.get(ContextKind::InvalidSubcommand);
            if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
                styled.none("'");
                styled.warning(invalid_sub);
                styled.none("' requires a subcommand but one was not provided");
                true
            } else {
                false
            }
        }
        ErrorKind::InvalidUtf8 => false,
        ErrorKind::TooManyValues => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let invalid_value = error.get(ContextKind::InvalidValue);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::String(invalid_value)),
            ) = (invalid_arg, invalid_value)
            {
                styled.none("The value '");
                styled.warning(invalid_value);
                styled.none("' was provided to '");
                styled.warning(invalid_arg);
                styled.none("' but it wasn't expecting any more values");
                true
            } else {
                false
            }
        }
        ErrorKind::TooFewValues => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let actual_num_values = error.get(ContextKind::ActualNumValues);
            let min_values = error.get(ContextKind::MinValues);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::Number(actual_num_values)),
                Some(ContextValue::Number(min_values)),
            ) = (invalid_arg, actual_num_values, min_values)
            {
                let were_provided = singular_or_plural(*actual_num_values as usize);
                styled.none("The argument '");
                styled.warning(invalid_arg);
                styled.none("' requires at least ");
                styled.warning(min_values.to_string());
                styled.none(" values but only ");
                styled.warning(actual_num_values.to_string());
                styled.none(were_provided);
                true
            } else {
                false
            }
        }
        ErrorKind::ValueValidation => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let invalid_value = error.get(ContextKind::InvalidValue);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::String(invalid_value)),
            ) = (invalid_arg, invalid_value)
            {
                styled.none("Invalid value '");
                styled.warning(invalid_value);
                styled.none("' for '");
                styled.warning(invalid_arg);
                if let Some(source) = error.inner.source.as_deref() {
                    styled.none("': ");
                    styled.none(source.to_string());
                } else {
                    styled.none("'");
                }
                true
            } else {
                false
            }
        }
        ErrorKind::WrongNumberOfValues => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let actual_num_values = error.get(ContextKind::ActualNumValues);
            let num_values = error.get(ContextKind::ExpectedNumValues);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::Number(actual_num_values)),
                Some(ContextValue::Number(num_values)),
            ) = (invalid_arg, actual_num_values, num_values)
            {
                let were_provided = singular_or_plural(*actual_num_values as usize);
                styled.none("The argument '");
                styled.warning(invalid_arg);
                styled.none("' requires ");
                styled.warning(num_values.to_string());
                styled.none(" values, but ");
                styled.warning(actual_num_values.to_string());
                styled.none(were_provided);
                true
            } else {
                false
            }
        }
        ErrorKind::UnknownArgument => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
                styled.none("Found argument '");
                styled.warning(invalid_arg.to_string());
                styled.none("' which wasn't expected, or isn't valid in this context");

                let valid_sub = error.get(ContextKind::SuggestedSubcommand);
                let valid_arg = error.get(ContextKind::SuggestedArg);
                match (valid_sub, valid_arg) {
                    (
                        Some(ContextValue::String(valid_sub)),
                        Some(ContextValue::String(valid_arg)),
                    ) => {
                        styled.none("\n\n");
                        styled.none(TAB);
                        styled.none("Did you mean to put '");
                        styled.good(valid_arg);
                        styled.none("' after the subcommand '");
                        styled.good(valid_sub);
                        styled.none("'?");
                    }
                    (None, Some(valid)) => {
                        styled.none("\n\n");
                        did_you_mean(styled, valid);
                    }
                    (_, _) => {}
                }

                let invalid_arg = error.get(ContextKind::InvalidArg);
                if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
                    let suggested_trailing_arg = error.get(ContextKind::SuggestedTrailingArg);
                    if suggested_trailing_arg == Some(&ContextValue::Bool(true)) {
                        styled.none("\n\n");
                        styled.none(TAB);
                        styled.none("If you tried to supply '");
                        styled.warning(invalid_arg);
                        styled.none("' as a value rather than a flag, use '");
                        styled.good("-- ");
                        styled.good(invalid_arg);
                        styled.none("'");
                    }

                    let trailing_arg = error.get(ContextKind::TrailingArg);
                    if trailing_arg == Some(&ContextValue::Bool(true)) {
                        styled.none("\n\n");
                        styled.none(TAB);
                        styled.none("If you tried to supply '");
                        styled.warning(invalid_arg);
                        styled.none("' as a subcommand, remove the '");
                        styled.warning("--");
                        styled.none("' before it.");
                    }
                }
                true
            } else {
                false
            }
        }
        ErrorKind::DisplayHelp
        | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
        | ErrorKind::DisplayVersion
        | ErrorKind::Io
        | ErrorKind::Format => false,
    }
}

pub(crate) fn format_error_message(
    message: &str,
    cmd: Option<&Command>,
    usage: Option<StyledStr>,
) -> StyledStr {
    let mut styled = StyledStr::new();
    start_error(&mut styled);
    styled.none(message);
    if let Some(usage) = usage {
        put_usage(&mut styled, usage);
    }
    if let Some(cmd) = cmd {
        try_help(&mut styled, get_help_flag(cmd));
    }
    styled
}

/// Returns the singular or plural form on the verb to be based on the argument's value.
fn singular_or_plural(n: usize) -> &'static str {
    if n > 1 {
        " were provided"
    } else {
        " was provided"
    }
}

fn put_usage(styled: &mut StyledStr, usage: StyledStr) {
    styled.none("\n\n");
    styled.extend(usage.into_iter());
}

pub(crate) fn get_help_flag(cmd: &Command) -> Option<&'static str> {
    if !cmd.is_disable_help_flag_set() {
        Some("--help")
    } else if cmd.has_subcommands() && !cmd.is_disable_help_subcommand_set() {
        Some("help")
    } else {
        None
    }
}

fn try_help(styled: &mut StyledStr, help: Option<&str>) {
    if let Some(help) = help {
        styled.none("\n\nFor more information try '");
        styled.literal(help.to_owned());
        styled.none("'\n");
    } else {
        styled.none("\n");
    }
}

#[cfg(feature = "error-context")]
fn did_you_mean(styled: &mut StyledStr, valid: &ContextValue) {
    if let ContextValue::String(valid) = valid {
        styled.none(TAB);
        styled.none("Did you mean '");
        styled.good(valid);
        styled.none("'?");
    } else if let ContextValue::Strings(valid) = valid {
        styled.none(TAB);
        styled.none("Did you mean ");
        for (i, valid) in valid.iter().enumerate() {
            if i != 0 {
                styled.none(", ");
            }
            styled.none("'");
            styled.good(valid);
            styled.none("'");
        }
        styled.none("?");
    }
}

fn escape(s: impl AsRef<str>) -> String {
    let s = s.as_ref();
    if s.contains(char::is_whitespace) {
        format!("{:?}", s)
    } else {
        s.to_owned()
    }
}
<commit_msg>refactor(error): Centralize suggestions<commit_after>#![allow(missing_copy_implementations)]
#![allow(missing_debug_implementations)]
#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]

use crate::builder::Command;
use crate::builder::StyledStr;
#[cfg(feature = "error-context")]
use crate::error::ContextKind;
#[cfg(feature = "error-context")]
use crate::error::ContextValue;
use crate::error::ErrorKind;
use crate::output::TAB;

/// Defines how to format an error for displaying to the user
pub trait ErrorFormatter: Sized {
    /// Stylize the error for the terminal
    fn format_error(error: &crate::error::Error<Self>) -> StyledStr;
}

/// Report [`ErrorKind`]
///
/// No context is included.
///
/// **NOTE:** Consider removing the [`error-context`][crate::_features] default feature if using this to remove all
/// overhead for [`RichFormatter`].
#[non_exhaustive]
pub struct KindFormatter;

impl ErrorFormatter for KindFormatter {
    fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
        let mut styled = StyledStr::new();
        start_error(&mut styled);
        if let Some(msg) = error.kind().as_str() {
            styled.none(msg.to_owned());
        } else if let Some(source) = error.inner.source.as_ref() {
            styled.none(source.to_string());
        } else {
            styled.none("Unknown cause");
        }
        styled.none("\n");
        styled
    }
}

/// Richly formatted error context
#[non_exhaustive]
#[cfg(feature = "error-context")]
pub struct RichFormatter;

#[cfg(feature = "error-context")]
impl ErrorFormatter for RichFormatter {
    fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
        let mut styled = StyledStr::new();
        start_error(&mut styled);

        if !write_dynamic_context(error, &mut styled) {
            if let Some(msg) = error.kind().as_str() {
                styled.none(msg.to_owned());
            } else if let Some(source) = error.inner.source.as_ref() {
                styled.none(source.to_string());
            } else {
                styled.none("Unknown cause");
            }
        }

        if let Some(valid) = error.get(ContextKind::SuggestedValue) {
            styled.none("\n\n");
            did_you_mean(&mut styled, valid);
        }

        let valid_sub = error.get(ContextKind::SuggestedSubcommand);
        let valid_arg = error.get(ContextKind::SuggestedArg);
        match (valid_sub, valid_arg) {
            (Some(ContextValue::String(valid_sub)), Some(ContextValue::String(valid_arg))) => {
                styled.none("\n\n");
                styled.none(TAB);
                styled.none("Did you mean to put '");
                styled.good(valid_arg);
                styled.none("' after the subcommand '");
                styled.good(valid_sub);
                styled.none("'?");
            }
            (Some(valid), None) | (None, Some(valid)) => {
                styled.none("\n\n");
                did_you_mean(&mut styled, valid);
            }
            (_, _) => {}
        }

        let invalid_arg = error.get(ContextKind::InvalidArg);
        if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
            let suggested_trailing_arg = error.get(ContextKind::SuggestedTrailingArg);
            if suggested_trailing_arg == Some(&ContextValue::Bool(true)) {
                styled.none("\n\n");
                styled.none(TAB);
                styled.none("If you tried to supply '");
                styled.warning(invalid_arg);
                styled.none("' as a value rather than a flag, use '");
                styled.good("-- ");
                styled.good(invalid_arg);
                styled.none("'");
            }

            let trailing_arg = error.get(ContextKind::TrailingArg);
            if trailing_arg == Some(&ContextValue::Bool(true)) {
                styled.none("\n\n");
                styled.none(TAB);
                styled.none("If you tried to supply '");
                styled.warning(invalid_arg);
                styled.none("' as a subcommand, remove the '");
                styled.warning("--");
                styled.none("' before it.");
            }
        }

        let suggestion = error.get(ContextKind::SuggestedCommand);
        if let Some(ContextValue::String(suggestion)) = suggestion {
            styled.none("\n\n");
            styled.none(TAB);
            styled.none("If you believe you received this message in error, try re-running with '");
            styled.good(suggestion);
            styled.none("'");
        }

        let usage = error.get(ContextKind::Usage);
        if let Some(ContextValue::StyledStr(usage)) = usage {
            put_usage(&mut styled, usage.clone());
        }

        try_help(&mut styled, error.inner.help_flag);

        styled
    }
}

fn start_error(styled: &mut StyledStr) {
    styled.error("error:");
    styled.none(" ");
}

#[must_use]
#[cfg(feature = "error-context")]
fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) -> bool {
    match error.kind() {
        ErrorKind::ArgumentConflict => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let prior_arg = error.get(ContextKind::PriorArg);
            if let (Some(ContextValue::String(invalid_arg)), Some(prior_arg)) =
                (invalid_arg, prior_arg)
            {
                if ContextValue::String(invalid_arg.clone()) == *prior_arg {
                    styled.none("The argument '");
                    styled.warning(invalid_arg);
                    styled.none("' was provided more than once, but cannot be used multiple times");
                } else {
                    styled.none("The argument '");
                    styled.warning(invalid_arg);
                    styled.none("' cannot be used with");

                    match prior_arg {
                        ContextValue::Strings(values) => {
                            styled.none(":");
                            for v in values {
                                styled.none("\n");
                                styled.none(TAB);
                                styled.warning(&**v);
                            }
                        }
                        ContextValue::String(value) => {
                            styled.none(" '");
                            styled.warning(value);
                            styled.none("'");
                        }
                        _ => {
                            styled.none(" one or more of the other specified arguments");
                        }
                    }
                }
                true
            } else {
                false
            }
        }
        ErrorKind::NoEquals => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
                styled.none("Equal sign is needed when assigning values to '");
                styled.warning(invalid_arg);
                styled.none("'.");
                true
            } else {
                false
            }
        }
        ErrorKind::InvalidValue => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let invalid_value = error.get(ContextKind::InvalidValue);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::String(invalid_value)),
            ) = (invalid_arg, invalid_value)
            {
                if invalid_value.is_empty() {
                    styled.none("The argument '");
                    styled.warning(invalid_arg);
                    styled.none("' requires a value but none was supplied");
                } else {
                    styled.none("'");
                    styled.none(invalid_value);
                    styled.none("' isn't a valid value for '");
                    styled.warning(invalid_arg);
                    styled.none("'");
                }

                let possible_values = error.get(ContextKind::ValidValue);
                if let Some(ContextValue::Strings(possible_values)) = possible_values {
                    if !possible_values.is_empty() {
                        styled.none("\n");
                        styled.none(TAB);
                        styled.none("[possible values: ");
                        if let Some((last, elements)) = possible_values.split_last() {
                            for v in elements {
                                styled.good(escape(v));
                                styled.none(", ");
                            }
                            styled.good(escape(last));
                        }
                        styled.none("]");
                    }
                }
                true
            } else {
                false
            }
        }
        ErrorKind::InvalidSubcommand => {
            let invalid_sub = error.get(ContextKind::InvalidSubcommand);
            if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
                styled.none("The subcommand '");
                styled.warning(invalid_sub);
                styled.none("' wasn't recognized");
                true
            } else {
                false
            }
        }
        ErrorKind::MissingRequiredArgument => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg {
                styled.none("The following required arguments were not provided:");
                for v in invalid_arg {
                    styled.none("\n");
                    styled.none(TAB);
                    styled.good(&**v);
                }
                true
            } else {
                false
            }
        }
        ErrorKind::MissingSubcommand => {
            let invalid_sub = error.get(ContextKind::InvalidSubcommand);
            if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
                styled.none("'");
                styled.warning(invalid_sub);
                styled.none("' requires a subcommand but one was not provided");
                true
            } else {
                false
            }
        }
        ErrorKind::InvalidUtf8 => false,
        ErrorKind::TooManyValues => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let invalid_value = error.get(ContextKind::InvalidValue);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::String(invalid_value)),
            ) = (invalid_arg, invalid_value)
            {
                styled.none("The value '");
                styled.warning(invalid_value);
                styled.none("' was provided to '");
                styled.warning(invalid_arg);
                styled.none("' but it wasn't expecting any more values");
                true
            } else {
                false
            }
        }
        ErrorKind::TooFewValues => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let actual_num_values = error.get(ContextKind::ActualNumValues);
            let min_values = error.get(ContextKind::MinValues);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::Number(actual_num_values)),
                Some(ContextValue::Number(min_values)),
            ) = (invalid_arg, actual_num_values, min_values)
            {
                let were_provided = singular_or_plural(*actual_num_values as usize);
                styled.none("The argument '");
                styled.warning(invalid_arg);
                styled.none("' requires at least ");
                styled.warning(min_values.to_string());
                styled.none(" values but only ");
                styled.warning(actual_num_values.to_string());
                styled.none(were_provided);
                true
            } else {
                false
            }
        }
        ErrorKind::ValueValidation => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let invalid_value = error.get(ContextKind::InvalidValue);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::String(invalid_value)),
            ) = (invalid_arg, invalid_value)
            {
                styled.none("Invalid value '");
                styled.warning(invalid_value);
                styled.none("' for '");
                styled.warning(invalid_arg);
                if let Some(source) = error.inner.source.as_deref() {
                    styled.none("': ");
                    styled.none(source.to_string());
                } else {
                    styled.none("'");
                }
                true
            } else {
                false
            }
        }
        ErrorKind::WrongNumberOfValues => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            let actual_num_values = error.get(ContextKind::ActualNumValues);
            let num_values = error.get(ContextKind::ExpectedNumValues);
            if let (
                Some(ContextValue::String(invalid_arg)),
                Some(ContextValue::Number(actual_num_values)),
                Some(ContextValue::Number(num_values)),
            ) = (invalid_arg, actual_num_values, num_values)
            {
                let were_provided = singular_or_plural(*actual_num_values as usize);
                styled.none("The argument '");
                styled.warning(invalid_arg);
                styled.none("' requires ");
                styled.warning(num_values.to_string());
                styled.none(" values, but ");
                styled.warning(actual_num_values.to_string());
                styled.none(were_provided);
                true
            } else {
                false
            }
        }
        ErrorKind::UnknownArgument => {
            let invalid_arg = error.get(ContextKind::InvalidArg);
            if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
                styled.none("Found argument '");
                styled.warning(invalid_arg.to_string());
                styled.none("' which wasn't expected, or isn't valid in this context");
                true
            } else {
                false
            }
        }
        ErrorKind::DisplayHelp
        | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
        | ErrorKind::DisplayVersion
        | ErrorKind::Io
        | ErrorKind::Format => false,
    }
}

pub(crate) fn format_error_message(
    message: &str,
    cmd: Option<&Command>,
    usage: Option<StyledStr>,
) -> StyledStr {
    let mut styled = StyledStr::new();
    start_error(&mut styled);
    styled.none(message);
    if let Some(usage) = usage {
        put_usage(&mut styled, usage);
    }
    if let Some(cmd) = cmd {
        try_help(&mut styled, get_help_flag(cmd));
    }
    styled
}

/// Returns the singular or plural form on the verb to be based on the argument's value.
fn singular_or_plural(n: usize) -> &'static str {
    if n > 1 {
        " were provided"
    } else {
        " was provided"
    }
}

fn put_usage(styled: &mut StyledStr, usage: StyledStr) {
    styled.none("\n\n");
    styled.extend(usage.into_iter());
}

pub(crate) fn get_help_flag(cmd: &Command) -> Option<&'static str> {
    if !cmd.is_disable_help_flag_set() {
        Some("--help")
    } else if cmd.has_subcommands() && !cmd.is_disable_help_subcommand_set() {
        Some("help")
    } else {
        None
    }
}

fn try_help(styled: &mut StyledStr, help: Option<&str>) {
    if let Some(help) = help {
        styled.none("\n\nFor more information try '");
        styled.literal(help.to_owned());
        styled.none("'\n");
    } else {
        styled.none("\n");
    }
}

#[cfg(feature = "error-context")]
fn did_you_mean(styled: &mut StyledStr, valid: &ContextValue) {
    if let ContextValue::String(valid) = valid {
        styled.none(TAB);
        styled.none("Did you mean '");
        styled.good(valid);
        styled.none("'?");
    } else if let ContextValue::Strings(valid) = valid {
        styled.none(TAB);
        styled.none("Did you mean ");
        for (i, valid) in valid.iter().enumerate() {
            if i != 0 {
                styled.none(", ");
            }
            styled.none("'");
            styled.good(valid);
            styled.none("'");
        }
        styled.none("?");
    }
}

fn escape(s: impl AsRef<str>) -> String {
    let s = s.as_ref();
    if s.contains(char::is_whitespace) {
        format!("{:?}", s)
    } else {
        s.to_owned()
    }
}
<|endoftext|>


<commit_before>pub use self::image_exporter::ImageExporter;
use crate::texture::Texture;

mod image_exporter;

pub type ExportResult<T> = Result<T, String>;

pub trait Exporter<T: Texture> {
    type Output;

    fn export(texture: &T) -> ExportResult<Self::Output>;
}
<commit_msg>docs: Document exporter module contents<commit_after>//! Defines a trait for exporting [Texture]s to arbitrary data.
pub use self::image_exporter::ImageExporter;
use crate::texture::Texture;

mod image_exporter;

/// Result of exporting a texture.
pub type ExportResult<T> = Result<T, String>;

/// Converter for a [Texture] to type `Output`.
pub trait Exporter<T: Texture> {
    /// Output type to use.
    type Output;

    /// Export a texture object to an `Output`.
    fn export(texture: &T) -> ExportResult<Self::Output>;
}
<|endoftext|>


<commit_before>// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

use {ChannelLayout, DeviceRef, Result, SampleFormat};
use ffi;
use std::os::raw::c_void;
use std::ptr;

/// Stream states signaled via `state_callback`.
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum State {
    /// Stream started.
    Started,
    /// Stream stopped.
    Stopped,
    /// Stream drained.
    Drained,
    /// Stream disabled due to error.
    Error,
}

impl From<ffi::cubeb_state> for State {
    fn from(x: ffi::cubeb_state) -> Self {
        use State::*;
        match x {
            ffi::CUBEB_STATE_STARTED => Started,
            ffi::CUBEB_STATE_STOPPED => Stopped,
            ffi::CUBEB_STATE_DRAINED => Drained,
            _ => Error,
        }
    }
}

impl Into<ffi::cubeb_state> for State {
    fn into(self) -> ffi::cubeb_state {
        use State::*;
        match self {
            Started => ffi::CUBEB_STATE_STARTED,
            Stopped => ffi::CUBEB_STATE_STOPPED,
            Drained => ffi::CUBEB_STATE_DRAINED,
            Error => ffi::CUBEB_STATE_ERROR,
        }
    }
}

/// Miscellaneous stream preferences.
bitflags! {
    pub struct StreamPrefs: ffi::cubeb_stream_prefs {
        const NONE = ffi::CUBEB_STREAM_PREF_NONE;
        const LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
    }
}

/// Stream format initialization parameters.
ffi_type_stack!{
    type CType = ffi::cubeb_stream_params;
    #[derive(Debug)]
    pub struct StreamParams;
    pub struct StreamParamsRef;
}

impl StreamParamsRef {
    fn get_ref(&self) -> &ffi::cubeb_stream_params {
        unsafe { &*self.as_ptr() }
    }

    pub fn format(&self) -> SampleFormat {
        use SampleFormat::*;
        match self.get_ref().format {
            ffi::CUBEB_SAMPLE_S16LE => S16LE,
            ffi::CUBEB_SAMPLE_S16BE => S16BE,
            ffi::CUBEB_SAMPLE_FLOAT32LE => Float32LE,
            ffi::CUBEB_SAMPLE_FLOAT32BE => Float32BE,
            x => panic!("unknown sample format: {}", x),
        }
    }

    pub fn rate(&self) -> u32 {
        self.get_ref().rate
    }
    pub fn channels(&self) -> u32 {
        self.get_ref().channels
    }

    pub fn layout(&self) -> ChannelLayout {
        ChannelLayout::from(self.get_ref().layout)
    }

    pub fn prefs(&self) -> StreamPrefs {
        StreamPrefs::from_bits_truncate(self.get_ref().prefs)
    }
}

ffi_type_heap! {
    type CType = ffi::cubeb_stream;
    fn drop = ffi::cubeb_stream_destroy;
    pub struct Stream;
    pub struct StreamRef;
}

impl StreamRef {
    /// Start playback.
    pub fn start(&self) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_start(self.as_ptr())) }
    }

    /// Stop playback.
    pub fn stop(&self) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_stop(self.as_ptr())) }
    }

    /// Reset stream to the default device.
    pub fn reset_default_device(&self) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_reset_default_device(self.as_ptr())) }
    }

    /// Get the current stream playback position.
    pub fn position(&self) -> Result<u64> {
        let mut position = 0u64;
        unsafe {
            let _ = try_call!(ffi::cubeb_stream_get_position(self.as_ptr(), &mut position));
        }
        Ok(position)
    }

    /// Get the latency for this stream, in frames. This is the number
    /// of frames between the time cubeb acquires the data in the
    /// callback and the listener can hear the sound.
    pub fn latency(&self) -> Result<u32> {
        let mut latency = 0u32;
        unsafe {
            let _ = try_call!(ffi::cubeb_stream_get_latency(self.as_ptr(), &mut latency));
        }
        Ok(latency)
    }

    /// Set the volume for a stream.
    pub fn set_volume(&self, volume: f32) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
    }

    /// If the stream is stereo, set the left/right panning. If the stream is mono,
    /// this has no effect.
    ///
    /// panning a number from -1.0 to 1.0. -1.0 means that the stream is
    /// fully mixed in the left channel, 1.0 means the stream is fully
    /// mixed in the right channel. 0.0 is equal power in the right
    /// and left channel (default).
    pub fn set_panning(&self, panning: f32) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_set_panning(self.as_ptr(), panning)) }
    }

    /// Get the current output device for this stream.
    pub fn current_device(&self) -> Result<&DeviceRef> {
        let mut device: *mut ffi::cubeb_device = ptr::null_mut();
        unsafe {
            let _ = try_call!(ffi::cubeb_stream_get_current_device(
                self.as_ptr(),
                &mut device
            ));
            Ok(DeviceRef::from_ptr(device))
        }
    }

    /// Destroy a cubeb_device structure.
    pub fn device_destroy(&self, device: DeviceRef) -> Result<()> {
        unsafe {
            call!(ffi::cubeb_stream_device_destroy(
                self.as_ptr(),
                device.as_ptr()
            ))
        }
    }

    /// Set a callback to be notified when the output device changes.
    pub fn register_device_changed_callback(
        &self,
        callback: ffi::cubeb_device_changed_callback,
    ) -> Result<()> {
        unsafe {
            call!(ffi::cubeb_stream_register_device_changed_callback(
                self.as_ptr(),
                callback
            ))
        }
    }

    pub fn user_ptr(&self) -> *mut c_void {
        unsafe { ffi::cubeb_stream_user_ptr(self.as_ptr()) }
    }
}

#[cfg(test)]
mod tests {
    use {StreamParams, StreamParamsRef, StreamPrefs};
    use std::mem;

    #[test]
    fn stream_params_default() {
        let params = StreamParams::default();
        assert_eq!(params.channels(), 0);
    }

    #[test]
    fn stream_params_raw_channels() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.channels = 2;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(params.channels(), 2);
    }

    #[test]
    fn stream_params_raw_format() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        macro_rules! check(
            ($($raw:ident => $real:ident),*) => (
                $(raw.format = super::ffi::$raw;
                  let params = unsafe {
                      StreamParamsRef::from_ptr(&mut raw)
                  };
                  assert_eq!(params.format(), super::SampleFormat::$real);
                )*
            ) );

        check!(CUBEB_SAMPLE_S16LE => S16LE,
               CUBEB_SAMPLE_S16BE => S16BE,
               CUBEB_SAMPLE_FLOAT32LE => Float32LE,
               CUBEB_SAMPLE_FLOAT32BE => Float32BE);
    }

    #[test]
    fn stream_params_raw_format_native_endian() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.format = super::ffi::CUBEB_SAMPLE_S16NE;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(
            params.format(),
            if cfg!(target_endian = "little") {
                super::SampleFormat::S16LE
            } else {
                super::SampleFormat::S16BE
            }
        );

        raw.format = super::ffi::CUBEB_SAMPLE_FLOAT32NE;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(
            params.format(),
            if cfg!(target_endian = "little") {
                super::SampleFormat::Float32LE
            } else {
                super::SampleFormat::Float32BE
            }
        );
    }

    #[test]
    fn stream_params_raw_layout() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        macro_rules! check(
            ($($raw:ident => $real:ident),*) => (
                $(raw.layout = super::ffi::$raw;
                  let params = unsafe {
                      StreamParamsRef::from_ptr(&mut raw)
                  };
                  assert_eq!(params.layout(), super::ChannelLayout::$real);
                )*
            ) );

        check!(CUBEB_LAYOUT_UNDEFINED => UNDEFINED,
               CUBEB_LAYOUT_MONO => MONO,
               CUBEB_LAYOUT_MONO_LFE => MONO_LFE,
               CUBEB_LAYOUT_STEREO => STEREO,
               CUBEB_LAYOUT_STEREO_LFE => STEREO_LFE,
               CUBEB_LAYOUT_3F => _3F,
               CUBEB_LAYOUT_3F_LFE => _3F_LFE,
               CUBEB_LAYOUT_2F1 => _2F1,
               CUBEB_LAYOUT_2F1_LFE => _2F1_LFE,
               CUBEB_LAYOUT_3F1 => _3F1,
               CUBEB_LAYOUT_3F1_LFE => _3F1_LFE,
               CUBEB_LAYOUT_2F2 => _2F2,
               CUBEB_LAYOUT_2F2_LFE => _2F2_LFE,
               CUBEB_LAYOUT_QUAD => QUAD,
               CUBEB_LAYOUT_QUAD_LFE => QUAD_LFE,
               CUBEB_LAYOUT_3F2 => _3F2,
               CUBEB_LAYOUT_3F2_LFE => _3F2_LFE,
               CUBEB_LAYOUT_3F2_BACK => _3F2_BACK,
               CUBEB_LAYOUT_3F2_LFE_BACK => _3F2_LFE_BACK,
               CUBEB_LAYOUT_3F3R_LFE => _3F3R_LFE,
               CUBEB_LAYOUT_3F4_LFE => _3F4_LFE);
    }

    #[test]
    fn stream_params_raw_rate() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.rate = 44_100;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(params.rate(), 44_100);
    }

    #[test]
    fn stream_params_raw_prefs() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.prefs = super::ffi::CUBEB_STREAM_PREF_LOOPBACK;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(params.prefs(), StreamPrefs::LOOPBACK);
    }

    #[test]
    fn stream_params_stream_params_ref_ptr_match() {
        let params = StreamParams::default();
        assert_eq!(params.as_ptr(), params.as_ref().as_ptr());
    }
}
<commit_msg>cubeb-core: Stop cubeb_stream before destroying it in Drop.<commit_after>// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

use {ChannelLayout, DeviceRef, Result, SampleFormat};
use ffi;
use std::os::raw::c_void;
use std::ptr;

/// Stream states signaled via `state_callback`.
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum State {
    /// Stream started.
    Started,
    /// Stream stopped.
    Stopped,
    /// Stream drained.
    Drained,
    /// Stream disabled due to error.
    Error,
}

impl From<ffi::cubeb_state> for State {
    fn from(x: ffi::cubeb_state) -> Self {
        use State::*;
        match x {
            ffi::CUBEB_STATE_STARTED => Started,
            ffi::CUBEB_STATE_STOPPED => Stopped,
            ffi::CUBEB_STATE_DRAINED => Drained,
            _ => Error,
        }
    }
}

impl Into<ffi::cubeb_state> for State {
    fn into(self) -> ffi::cubeb_state {
        use State::*;
        match self {
            Started => ffi::CUBEB_STATE_STARTED,
            Stopped => ffi::CUBEB_STATE_STOPPED,
            Drained => ffi::CUBEB_STATE_DRAINED,
            Error => ffi::CUBEB_STATE_ERROR,
        }
    }
}

/// Miscellaneous stream preferences.
bitflags! {
    pub struct StreamPrefs: ffi::cubeb_stream_prefs {
        const NONE = ffi::CUBEB_STREAM_PREF_NONE;
        const LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
    }
}

/// Stream format initialization parameters.
ffi_type_stack!{
    type CType = ffi::cubeb_stream_params;
    #[derive(Debug)]
    pub struct StreamParams;
    pub struct StreamParamsRef;
}

impl StreamParamsRef {
    fn get_ref(&self) -> &ffi::cubeb_stream_params {
        unsafe { &*self.as_ptr() }
    }

    pub fn format(&self) -> SampleFormat {
        use SampleFormat::*;
        match self.get_ref().format {
            ffi::CUBEB_SAMPLE_S16LE => S16LE,
            ffi::CUBEB_SAMPLE_S16BE => S16BE,
            ffi::CUBEB_SAMPLE_FLOAT32LE => Float32LE,
            ffi::CUBEB_SAMPLE_FLOAT32BE => Float32BE,
            x => panic!("unknown sample format: {}", x),
        }
    }

    pub fn rate(&self) -> u32 {
        self.get_ref().rate
    }
    pub fn channels(&self) -> u32 {
        self.get_ref().channels
    }

    pub fn layout(&self) -> ChannelLayout {
        ChannelLayout::from(self.get_ref().layout)
    }

    pub fn prefs(&self) -> StreamPrefs {
        StreamPrefs::from_bits_truncate(self.get_ref().prefs)
    }
}

unsafe fn wrapped_cubeb_stream_destroy(stream: *mut ffi::cubeb_stream) {
    ffi::cubeb_stream_stop(stream);
    ffi::cubeb_stream_destroy(stream);
}

ffi_type_heap! {
    type CType = ffi::cubeb_stream;
    fn drop = wrapped_cubeb_stream_destroy;
    pub struct Stream;
    pub struct StreamRef;
}

impl StreamRef {
    /// Start playback.
    pub fn start(&self) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_start(self.as_ptr())) }
    }

    /// Stop playback.
    pub fn stop(&self) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_stop(self.as_ptr())) }
    }

    /// Reset stream to the default device.
    pub fn reset_default_device(&self) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_reset_default_device(self.as_ptr())) }
    }

    /// Get the current stream playback position.
    pub fn position(&self) -> Result<u64> {
        let mut position = 0u64;
        unsafe {
            let _ = try_call!(ffi::cubeb_stream_get_position(self.as_ptr(), &mut position));
        }
        Ok(position)
    }

    /// Get the latency for this stream, in frames. This is the number
    /// of frames between the time cubeb acquires the data in the
    /// callback and the listener can hear the sound.
    pub fn latency(&self) -> Result<u32> {
        let mut latency = 0u32;
        unsafe {
            let _ = try_call!(ffi::cubeb_stream_get_latency(self.as_ptr(), &mut latency));
        }
        Ok(latency)
    }

    /// Set the volume for a stream.
    pub fn set_volume(&self, volume: f32) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
    }

    /// If the stream is stereo, set the left/right panning. If the stream is mono,
    /// this has no effect.
    ///
    /// panning a number from -1.0 to 1.0. -1.0 means that the stream is
    /// fully mixed in the left channel, 1.0 means the stream is fully
    /// mixed in the right channel. 0.0 is equal power in the right
    /// and left channel (default).
    pub fn set_panning(&self, panning: f32) -> Result<()> {
        unsafe { call!(ffi::cubeb_stream_set_panning(self.as_ptr(), panning)) }
    }

    /// Get the current output device for this stream.
    pub fn current_device(&self) -> Result<&DeviceRef> {
        let mut device: *mut ffi::cubeb_device = ptr::null_mut();
        unsafe {
            let _ = try_call!(ffi::cubeb_stream_get_current_device(
                self.as_ptr(),
                &mut device
            ));
            Ok(DeviceRef::from_ptr(device))
        }
    }

    /// Destroy a cubeb_device structure.
    pub fn device_destroy(&self, device: DeviceRef) -> Result<()> {
        unsafe {
            call!(ffi::cubeb_stream_device_destroy(
                self.as_ptr(),
                device.as_ptr()
            ))
        }
    }

    /// Set a callback to be notified when the output device changes.
    pub fn register_device_changed_callback(
        &self,
        callback: ffi::cubeb_device_changed_callback,
    ) -> Result<()> {
        unsafe {
            call!(ffi::cubeb_stream_register_device_changed_callback(
                self.as_ptr(),
                callback
            ))
        }
    }

    pub fn user_ptr(&self) -> *mut c_void {
        unsafe { ffi::cubeb_stream_user_ptr(self.as_ptr()) }
    }
}

#[cfg(test)]
mod tests {
    use {StreamParams, StreamParamsRef, StreamPrefs};
    use std::mem;

    #[test]
    fn stream_params_default() {
        let params = StreamParams::default();
        assert_eq!(params.channels(), 0);
    }

    #[test]
    fn stream_params_raw_channels() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.channels = 2;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(params.channels(), 2);
    }

    #[test]
    fn stream_params_raw_format() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        macro_rules! check(
            ($($raw:ident => $real:ident),*) => (
                $(raw.format = super::ffi::$raw;
                  let params = unsafe {
                      StreamParamsRef::from_ptr(&mut raw)
                  };
                  assert_eq!(params.format(), super::SampleFormat::$real);
                )*
            ) );

        check!(CUBEB_SAMPLE_S16LE => S16LE,
               CUBEB_SAMPLE_S16BE => S16BE,
               CUBEB_SAMPLE_FLOAT32LE => Float32LE,
               CUBEB_SAMPLE_FLOAT32BE => Float32BE);
    }

    #[test]
    fn stream_params_raw_format_native_endian() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.format = super::ffi::CUBEB_SAMPLE_S16NE;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(
            params.format(),
            if cfg!(target_endian = "little") {
                super::SampleFormat::S16LE
            } else {
                super::SampleFormat::S16BE
            }
        );

        raw.format = super::ffi::CUBEB_SAMPLE_FLOAT32NE;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(
            params.format(),
            if cfg!(target_endian = "little") {
                super::SampleFormat::Float32LE
            } else {
                super::SampleFormat::Float32BE
            }
        );
    }

    #[test]
    fn stream_params_raw_layout() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        macro_rules! check(
            ($($raw:ident => $real:ident),*) => (
                $(raw.layout = super::ffi::$raw;
                  let params = unsafe {
                      StreamParamsRef::from_ptr(&mut raw)
                  };
                  assert_eq!(params.layout(), super::ChannelLayout::$real);
                )*
            ) );

        check!(CUBEB_LAYOUT_UNDEFINED => UNDEFINED,
               CUBEB_LAYOUT_MONO => MONO,
               CUBEB_LAYOUT_MONO_LFE => MONO_LFE,
               CUBEB_LAYOUT_STEREO => STEREO,
               CUBEB_LAYOUT_STEREO_LFE => STEREO_LFE,
               CUBEB_LAYOUT_3F => _3F,
               CUBEB_LAYOUT_3F_LFE => _3F_LFE,
               CUBEB_LAYOUT_2F1 => _2F1,
               CUBEB_LAYOUT_2F1_LFE => _2F1_LFE,
               CUBEB_LAYOUT_3F1 => _3F1,
               CUBEB_LAYOUT_3F1_LFE => _3F1_LFE,
               CUBEB_LAYOUT_2F2 => _2F2,
               CUBEB_LAYOUT_2F2_LFE => _2F2_LFE,
               CUBEB_LAYOUT_QUAD => QUAD,
               CUBEB_LAYOUT_QUAD_LFE => QUAD_LFE,
               CUBEB_LAYOUT_3F2 => _3F2,
               CUBEB_LAYOUT_3F2_LFE => _3F2_LFE,
               CUBEB_LAYOUT_3F2_BACK => _3F2_BACK,
               CUBEB_LAYOUT_3F2_LFE_BACK => _3F2_LFE_BACK,
               CUBEB_LAYOUT_3F3R_LFE => _3F3R_LFE,
               CUBEB_LAYOUT_3F4_LFE => _3F4_LFE);
    }

    #[test]
    fn stream_params_raw_rate() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.rate = 44_100;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(params.rate(), 44_100);
    }

    #[test]
    fn stream_params_raw_prefs() {
        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
        raw.prefs = super::ffi::CUBEB_STREAM_PREF_LOOPBACK;
        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
        assert_eq!(params.prefs(), StreamPrefs::LOOPBACK);
    }

    #[test]
    fn stream_params_stream_params_ref_ptr_match() {
        let params = StreamParams::default();
        assert_eq!(params.as_ptr(), params.as_ref().as_ptr());
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "use_std")]

use std::collections::HashMap;
use std::cmp::Ordering;
use std::hash::Hash;
use std::iter::Iterator;

/// A wrapper to allow for an easy [`into_grouping_map_by`](../trait.Itertools.html#method.into_grouping_map_by)
pub struct MapForGrouping<I, F>(I, F);

impl<I, F> MapForGrouping<I, F> {
    pub(crate) fn new(iter: I, key_mapper: F) -> Self {
        Self(iter, key_mapper)
    }
}

impl<K, V, I, F> Iterator for MapForGrouping<I, F>
    where I: Iterator<Item = V>,
          K: Hash + Eq,
          F: FnMut(&V) -> K,
{
    type Item = (K, V);
    fn next(&mut self) -> Option<Self::Item> {
        let val = self.0.next()?;
        Some(((self.1)(&val), val))
    }
}

/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
    where I: Iterator<Item = (K, V)>,
          K: Hash + Eq,
{
    GroupingMap { iter }
}

/// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
/// It groups elements by their key and at the same time fold each group
/// using some aggregating operation.
///
/// No method on this struct performs temporary allocations.
#[must_use = "GroupingMap is lazy and do nothing unless consumed"]
pub struct GroupingMap<I> {
    iter: I,
}

impl<I, K, V> GroupingMap<I>
    where I: Iterator<Item = (K, V)>,
          K: Hash + Eq,
{
    /// This is the generic way to perform any operations on a `GroupingMap`.
    /// It's suggested to use this method only to implement custom operations
    /// when the already provided ones are not enough.
    ///
    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in an `HashMap`.
    ///
    /// The `operation` function is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group if there is currently one;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being aggregated;
    /// If `operation` returns `Some(element)` then the accumulator is updated with `element`,
    /// otherwise the previous accumulation is discarded.
    ///
    /// Return a `HashMap` associating the key of each group with the result of aggregation of the group elements.
    /// If there's no result then there won't be an entry associated to that key.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10];
    /// let lookup = data.into_iter()
    ///     .into_grouping_map_by(|&n| n % 4)
    ///     .aggregate(|acc, _, val| {
    ///         if val == 0 || val == 10 {
    ///             None
    ///         } else {
    ///             Some(acc.unwrap_or(0) + val)
    ///         }
    ///     });
    ///
    /// assert_eq!(lookup[&0], 4);        // 0 resets the accumulator so only 4 is summed
    /// assert_eq!(lookup[&1], 14);       // 5 + 9
    /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward
    /// assert_eq!(lookup[&3], 7);        // 7
    /// assert_eq!(lookup.len(), 3);      // The final keys are only 0, 1 and 2
    /// ```
    pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
        where FO: FnMut(Option<R>, &K, V) -> Option<R>,
    {
        let mut destination_map = HashMap::new();

        for (key, val) in self.iter {
            let acc = destination_map.remove(&key);
            if let Some(op_res) = operation(acc, &key, val) {
                destination_map.insert(key, op_res);
            }
        }

        destination_map
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// `init` is the value from which will be cloned the initial value of each accumulator.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = (1..=7)
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .fold(0, |acc, _, val| acc + val);
    ///
    /// assert_eq!(lookup[&0], 9);   // 3 + 6
    /// assert_eq!(lookup[&1], 12);  // 1 + 4 + 7
    /// assert_eq!(lookup[&2], 7);   // 2 + 5
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
        where R: Clone,
              FO: FnMut(R, &K, V) -> R,
    {
        self.aggregate(|acc, key, val| {
            let acc = acc.unwrap_or_else(|| init.clone());
            Some(operation(acc, key, val))
        })
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// [`fold`]: #tymethod.fold
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = (1..=7)
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .fold_first(|acc, _, val| acc + val);
    ///
    /// assert_eq!(lookup[&0], 9);   // 3 + 6
    /// assert_eq!(lookup[&1], 12);  // 1 + 4 + 7
    /// assert_eq!(lookup[&2], 7);   // 2 + 5
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
        where FO: FnMut(V, &K, V) -> V,
    {
        self.aggregate(|acc, key, val| {
            Some(match acc {
                Some(acc) => operation(acc, key, val),
                None => val,
            })
        })
    }

    /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
    /// an instance of `C`. The iteration order is preserved when inserting elements.
    ///
    /// Return a `HashMap` associating the key of each group with the collection containing the elements of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    /// use std::collections::HashSet;
    ///
    /// let lookup = vec![0, 1, 2, 3, 4, 5, 6, 2, 3, 6].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .collect::<HashSet<_>>();
    ///
    /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect());
    /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect());
    /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect());
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn collect<C>(self) -> HashMap<K, C>
        where C: Default + Extend<V>,
    {
        self.aggregate(|acc, _, v| {
            let mut acc = acc.unwrap_or_else(C::default);
            acc.extend(Some(v));
            Some(acc)
        })
    }

    /// Groups elements from the `GroupingMap` source by key and counts them.
    ///
    /// Return a `HashMap` associating the key of each group with the number of elements in that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = "This is a string".chars()
    ///     .into_grouping_map_by(|&c| c)
    ///     .count();
    ///
    /// assert_eq!(lookup[&'T'], 1);
    /// assert_eq!(lookup[&'h'], 1);
    /// assert_eq!(lookup[&'i'], 3);
    /// assert_eq!(lookup[&'s'], 3);
    /// assert_eq!(lookup[&' '], 3);
    /// assert_eq!(lookup[&'a'], 1);
    /// assert_eq!(lookup[&'t'], 1);
    /// assert_eq!(lookup[&'r'], 1);
    /// assert_eq!(lookup[&'n'], 1);
    /// assert_eq!(lookup[&'g'], 1);
    /// assert_eq!(lookup.len(), 10);
    /// ```
    pub fn count(self) -> HashMap<K, usize> {
        self.fold(0, |acc, _, _| acc + 1)
    }

    pub fn max(self) -> HashMap<K, V>
        where V: Ord,
    {
        self.fold_first(|acc, _, val| std::cmp::max(acc, val))
    }

    pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
        where F: FnMut(&V, &V) -> Ordering,
    {
        self.fold_first(|acc, _, val| match compare(&acc, &val) {
            Ordering::Less | Ordering::Equal => val,
            Ordering::Greater => acc
        })
    }

    pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
        where F: FnMut(&V) -> CK,
              CK: Ord,
    {
        self.max_by(|v1, v2| f(&v1).cmp(&f(&v2)))
    }

    pub fn min(self) -> HashMap<K, V>
        where V: Ord,
    {
        self.fold_first(|acc, _, val| std::cmp::min(acc, val))
    }

    pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
        where F: FnMut(&V, &V) -> Ordering,
    {
        self.fold_first(|acc, _, val| match compare(&acc, &val) {
            Ordering::Less | Ordering::Equal => acc,
            Ordering::Greater => val
        })
    }

    pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
        where F: FnMut(&V) -> CK,
              CK: Ord,
    {
        self.min_by(|v1, v2| f(&v1).cmp(&f(&v2)))
    }
}
<commit_msg>Added docs and doctests for GroupingMap::{max, max_by, max_by_key, min, min_by, min_by_key}<commit_after>#![cfg(feature = "use_std")]

use std::collections::HashMap;
use std::cmp::Ordering;
use std::hash::Hash;
use std::iter::Iterator;

/// A wrapper to allow for an easy [`into_grouping_map_by`](../trait.Itertools.html#method.into_grouping_map_by)
pub struct MapForGrouping<I, F>(I, F);

impl<I, F> MapForGrouping<I, F> {
    pub(crate) fn new(iter: I, key_mapper: F) -> Self {
        Self(iter, key_mapper)
    }
}

impl<K, V, I, F> Iterator for MapForGrouping<I, F>
    where I: Iterator<Item = V>,
          K: Hash + Eq,
          F: FnMut(&V) -> K,
{
    type Item = (K, V);
    fn next(&mut self) -> Option<Self::Item> {
        let val = self.0.next()?;
        Some(((self.1)(&val), val))
    }
}

/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
    where I: Iterator<Item = (K, V)>,
          K: Hash + Eq,
{
    GroupingMap { iter }
}

/// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
/// It groups elements by their key and at the same time fold each group
/// using some aggregating operation.
///
/// No method on this struct performs temporary allocations.
#[must_use = "GroupingMap is lazy and do nothing unless consumed"]
pub struct GroupingMap<I> {
    iter: I,
}

impl<I, K, V> GroupingMap<I>
    where I: Iterator<Item = (K, V)>,
          K: Hash + Eq,
{
    /// This is the generic way to perform any operations on a `GroupingMap`.
    /// It's suggested to use this method only to implement custom operations
    /// when the already provided ones are not enough.
    ///
    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in an `HashMap`.
    ///
    /// The `operation` function is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group if there is currently one;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being aggregated;
    /// If `operation` returns `Some(element)` then the accumulator is updated with `element`,
    /// otherwise the previous accumulation is discarded.
    ///
    /// Return a `HashMap` associating the key of each group with the result of aggregation of the group elements.
    /// If there's no result then there won't be an entry associated to that key.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10];
    /// let lookup = data.into_iter()
    ///     .into_grouping_map_by(|&n| n % 4)
    ///     .aggregate(|acc, _, val| {
    ///         if val == 0 || val == 10 {
    ///             None
    ///         } else {
    ///             Some(acc.unwrap_or(0) + val)
    ///         }
    ///     });
    ///
    /// assert_eq!(lookup[&0], 4);        // 0 resets the accumulator so only 4 is summed
    /// assert_eq!(lookup[&1], 14);       // 5 + 9
    /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward
    /// assert_eq!(lookup[&3], 7);        // 7
    /// assert_eq!(lookup.len(), 3);      // The final keys are only 0, 1 and 2
    /// ```
    pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
        where FO: FnMut(Option<R>, &K, V) -> Option<R>,
    {
        let mut destination_map = HashMap::new();

        for (key, val) in self.iter {
            let acc = destination_map.remove(&key);
            if let Some(op_res) = operation(acc, &key, val) {
                destination_map.insert(key, op_res);
            }
        }

        destination_map
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// `init` is the value from which will be cloned the initial value of each accumulator.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = (1..=7)
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .fold(0, |acc, _, val| acc + val);
    ///
    /// assert_eq!(lookup[&0], 9);   // 3 + 6
    /// assert_eq!(lookup[&1], 12);  // 1 + 4 + 7
    /// assert_eq!(lookup[&2], 7);   // 2 + 5
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
        where R: Clone,
              FO: FnMut(R, &K, V) -> R,
    {
        self.aggregate(|acc, key, val| {
            let acc = acc.unwrap_or_else(|| init.clone());
            Some(operation(acc, key, val))
        })
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// [`fold`]: #tymethod.fold
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = (1..=7)
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .fold_first(|acc, _, val| acc + val);
    ///
    /// assert_eq!(lookup[&0], 9);   // 3 + 6
    /// assert_eq!(lookup[&1], 12);  // 1 + 4 + 7
    /// assert_eq!(lookup[&2], 7);   // 2 + 5
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
        where FO: FnMut(V, &K, V) -> V,
    {
        self.aggregate(|acc, key, val| {
            Some(match acc {
                Some(acc) => operation(acc, key, val),
                None => val,
            })
        })
    }

    /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
    /// an instance of `C`. The iteration order is preserved when inserting elements.
    ///
    /// Return a `HashMap` associating the key of each group with the collection containing the elements of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    /// use std::collections::HashSet;
    ///
    /// let lookup = vec![0, 1, 2, 3, 4, 5, 6, 2, 3, 6].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .collect::<HashSet<_>>();
    ///
    /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect());
    /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect());
    /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect());
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn collect<C>(self) -> HashMap<K, C>
        where C: Default + Extend<V>,
    {
        self.aggregate(|acc, _, v| {
            let mut acc = acc.unwrap_or_else(C::default);
            acc.extend(Some(v));
            Some(acc)
        })
    }

    /// Groups elements from the `GroupingMap` source by key and counts them.
    ///
    /// Return a `HashMap` associating the key of each group with the number of elements in that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = "This is a string".chars()
    ///     .into_grouping_map_by(|&c| c)
    ///     .count();
    ///
    /// assert_eq!(lookup[&'T'], 1);
    /// assert_eq!(lookup[&'h'], 1);
    /// assert_eq!(lookup[&'i'], 3);
    /// assert_eq!(lookup[&'s'], 3);
    /// assert_eq!(lookup[&' '], 3);
    /// assert_eq!(lookup[&'a'], 1);
    /// assert_eq!(lookup[&'t'], 1);
    /// assert_eq!(lookup[&'r'], 1);
    /// assert_eq!(lookup[&'n'], 1);
    /// assert_eq!(lookup[&'g'], 1);
    /// assert_eq!(lookup.len(), 10);
    /// ```
    pub fn count(self) -> HashMap<K, usize> {
        self.fold(0, |acc, _, _| acc + 1)
    }

    /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group.
    ///
    /// If several elements are equally maximum, the last element is picked.
    ///
    /// Returns a `HashMap` associating the key of each group with the maximum of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .max();
    ///
    /// assert_eq!(lookup[&0], 12);
    /// assert_eq!(lookup[&1], 7);
    /// assert_eq!(lookup[&2], 8);
    /// ```
    pub fn max(self) -> HashMap<K, V>
        where V: Ord,
    {
        self.fold_first(|acc, _, val| std::cmp::max(acc, val))
    }

    /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group
    /// with respect to the specified comparison function.
    ///
    /// If several elements are equally maximum, the last element is picked.
    ///
    /// Returns a `HashMap` associating the key of each group with the maximum of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .max_by(|x, y| y.cmp(x));
    ///
    /// assert_eq!(lookup[&0], 3);
    /// assert_eq!(lookup[&1], 1);
    /// assert_eq!(lookup[&2], 5);
    /// ```
    pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
        where F: FnMut(&V, &V) -> Ordering,
    {
        self.fold_first(|acc, _, val| match compare(&acc, &val) {
            Ordering::Less | Ordering::Equal => val,
            Ordering::Greater => acc
        })
    }

    /// Groups elements from the `GroupingMap` source by key and finds the element of each group
    /// that gives the maximum from the specified function.
    ///
    /// If several elements are equally maximum, the last element is picked.
    ///
    /// Returns a `HashMap` associating the key of each group with the maximum of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .max_by_key(|&val| val % 4);
    ///
    /// assert_eq!(lookup[&0], 3);
    /// assert_eq!(lookup[&1], 7);
    /// assert_eq!(lookup[&2], 5);
    /// ```
    pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
        where F: FnMut(&V) -> CK,
              CK: Ord,
    {
        self.max_by(|v1, v2| f(&v1).cmp(&f(&v2)))
    }

    /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group.
    ///
    /// If several elements are equally minimum, the first element is picked.
    ///
    /// Returns a `HashMap` associating the key of each group with the minimum of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .min();
    ///
    /// assert_eq!(lookup[&0], 3);
    /// assert_eq!(lookup[&1], 1);
    /// assert_eq!(lookup[&2], 5);
    /// ```
    pub fn min(self) -> HashMap<K, V>
        where V: Ord,
    {
        self.fold_first(|acc, _, val| std::cmp::min(acc, val))
    }

    /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group
    /// with respect to the specified comparison function.
    ///
    /// If several elements are equally minimum, the first element is picked.
    ///
    /// Returns a `HashMap` associating the key of each group with the minimum of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .min_by(|x, y| y.cmp(x));
    ///
    /// assert_eq!(lookup[&0], 12);
    /// assert_eq!(lookup[&1], 7);
    /// assert_eq!(lookup[&2], 8);
    /// ```
    pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
        where F: FnMut(&V, &V) -> Ordering,
    {
        self.fold_first(|acc, _, val| match compare(&acc, &val) {
            Ordering::Less | Ordering::Equal => acc,
            Ordering::Greater => val
        })
    }

    /// Groups elements from the `GroupingMap` source by key and finds the element of each group
    /// that gives the minimum from the specified function.
    ///
    /// If several elements are equally minimum, the first element is picked.
    ///
    /// Returns a `HashMap` associating the key of each group with the minimum of that group.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
    ///     .into_grouping_map_by(|&n| n % 3)
    ///     .min_by_key(|&val| val % 4);
    ///
    /// assert_eq!(lookup[&0], 12);
    /// assert_eq!(lookup[&1], 4);
    /// assert_eq!(lookup[&2], 8);
    /// ```
    pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
        where F: FnMut(&V) -> CK,
              CK: Ord,
    {
        self.min_by(|v1, v2| f(&v1).cmp(&f(&v2)))
    }
}
<|endoftext|>


<commit_before>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::{Orientation, RawImage};

extern crate time;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

fn decoder(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> OpBuffer {
  // First we check if the image's orientation results in a rotation that
  // swaps the maximum width with the maximum height
  let (transpose, ..) = img.orientation.to_flips();
  let (maxwidth, maxheight) = if transpose {
    (maxheight, maxwidth)
  } else {
    (maxwidth, maxheight)
  };

  let input = do_timing("gofloat", ||gofloat::convert(img));

  // Demosaic into 4 channel f32 (RGB or RGBE)
  let mut channel4 = do_timing("demosaic", ||demosaic::demosaic_and_scale(img, &input, maxwidth, maxheight));

  // Fix orientation if necessary and possible
  if img.orientation != Orientation::Normal && img.orientation != Orientation::Unknown {
    channel4 = do_timing("rotate", || { transform::rotate(img, &channel4) });
  }

  do_timing("level_and_balance", || { level::level_and_balance(img, &mut channel4) });
  // From now on we are in 3 channel f32 (RGB or Lab)
  let mut channel3 = do_timing("camera_to_lab", ||colorspaces::camera_to_lab(img, &channel4));
  do_timing("base_curve", ||curves::base(img, &mut channel3));
  do_timing("lab_to_rec709", ||colorspaces::lab_to_rec709(img, &mut channel3));
  if !linear {
    do_timing("gamma", ||gamma::gamma(img, &mut channel3));
  }

  channel3
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  decoder(img, maxwidth, maxheight, false)
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  decoder(img, maxwidth, maxheight, true)
}
<commit_msg>Improve rotation performance by putting it further down the decoding pipeline. It now operates on 3 rather than 4 channels<commit_after>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::{Orientation, RawImage};

extern crate time;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

fn decoder(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> OpBuffer {
  // First we check if the image's orientation results in a rotation that
  // swaps the maximum width with the maximum height
  let (transpose, ..) = img.orientation.to_flips();
  let (maxwidth, maxheight) = if transpose {
    (maxheight, maxwidth)
  } else {
    (maxwidth, maxheight)
  };

  let input = do_timing("gofloat", ||gofloat::convert(img));

  // Demosaic into 4 channel f32 (RGB or RGBE)
  let mut channel4 = do_timing("demosaic", ||demosaic::demosaic_and_scale(img, &input, maxwidth, maxheight));

  do_timing("level_and_balance", || { level::level_and_balance(img, &mut channel4) });
  // From now on we are in 3 channel f32 (RGB or Lab)
  let mut channel3 = do_timing("camera_to_lab", ||colorspaces::camera_to_lab(img, &channel4));
  do_timing("base_curve", ||curves::base(img, &mut channel3));
  do_timing("lab_to_rec709", ||colorspaces::lab_to_rec709(img, &mut channel3));
  if !linear {
    do_timing("gamma", ||gamma::gamma(img, &mut channel3));
  }

  // Fix orientation if necessary and possible
  if img.orientation != Orientation::Normal && img.orientation != Orientation::Unknown {
    do_timing("rotate", || { transform::rotate(img, &channel3) })
  } else {
    channel3
  }
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  decoder(img, maxwidth, maxheight, false)
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  decoder(img, maxwidth, maxheight, true)
}
<|endoftext|>


<commit_before>use std::rt::io::{Reader, Writer};
use std::rt::io::extensions::ReaderUtil;
use std::rt::io::{io_error, OtherIoError, IoError};
use client::request::RequestWriter;
use rfc2616::{CR, LF, SP};
use common::read_http_version;
use headers;
use status::Status;

use buffer::BufferedStream;
use server::request::{RequestBuffer};
use headers::{EndOfFile, EndOfHeaders, MalformedHeaderSyntax, MalformedHeaderValue};

struct ResponseReader<S> {
    priv stream: BufferedStream<S>,

    /// The request which this is a response to
    request: ~RequestWriter<S>,

    /// The HTTP version number; typically `(1, 1)` or, less commonly, `(1, 0)`.
    version: (uint, uint),

    /// The HTTP status indicated in the response.
    status: Status,

    /// The headers received in the response.
    headers: ~headers::response::HeaderCollection,
}

fn bad_response_err() -> IoError {
    // TODO: IoError isn't right
    IoError {
        kind: OtherIoError,
        desc: "Server returned malformed HTTP response",
        detail: None,
    }
}

impl<S: Reader + Writer> ResponseReader<S> {
    pub fn construct(mut stream: BufferedStream<S>, request: ~RequestWriter<S>)
            -> Result<ResponseReader<S>, ~RequestWriter<S>> {
        // TODO: raise condition at the points where Err is returned
        //let mut b = [0u8, ..4096];
        //let len = stream.read(b);
        //printfln!("%?", ::std::str::from_bytes(b.slice_to(len.unwrap())));
        let http_version = match read_http_version(&mut stream, SP) {
            Some(nums) => nums,
            None => {
                io_error::cond.raise(bad_response_err());
                return Err(request);
            }
        };

        // Read the status code
        let mut digits = 0u8;
        let mut status_code = 0u16;
        loop {
            if digits == 4u8 {
                // Status code must be three digits long
                io_error::cond.raise(bad_response_err());
                return Err(request);
            }
            match stream.read_byte() {
                Some(b) if b >= '0' as u8 && b <= '9' as u8 => {
                    status_code = status_code * 10 + b as u16 - '0' as u16;
                },
                Some(b) if b == SP => break,
                _ => {
                    io_error::cond.raise(bad_response_err());
                    return Err(request);
                }
            }
            digits += 1;
        }

        // Read the status reason
        let mut reason = ~"";
        loop {
            match stream.read_byte() {
                Some(b) if b == CR => {
                    if stream.read_byte() == Some(LF) {
                        break;
                    } else {
                        // Response-Line has CR without LF. Not yet resilient; TODO.
                        io_error::cond.raise(bad_response_err());
                        return Err(request);
                    }
                }
                Some(b) => {
                    reason.push_char(b as char);
                }
                None => {
                    io_error::cond.raise(bad_response_err());
                    return Err(request);
                }
            }
        }

        // Now we sneakily slip back to server::RequestBuffer to avoid code duplication. This is
        // temporary, honest!
        //
        // You see, read_header and read_header_line will be replaced, as will this. The code will
        // not be shared between them as they will have ultra-smart parsers (probably using Ragel)
        // to provide fast loading of standard headers, and the set of defined headers is distinct
        // between a request and response.
        let headers = {
            let mut buffer = RequestBuffer::new(&mut stream);
            let mut headers = ~headers::response::HeaderCollection::new();
            loop {
                let xxx = buffer.read_header::<headers::response::Header>();
                printfln!("xxx = %?", xxx);
                match xxx {
                //match buffer.read_header::<headers::response::Header>() {
                    Err(EndOfFile) => {
                        io_error::cond.raise(bad_response_err());
                        //fail!("server disconnected, no more response to receive :-(");
                        return Err(request);
                    },
                    Err(EndOfHeaders) => break,
                    Err(MalformedHeaderSyntax) => {
                        io_error::cond.raise(bad_response_err());
                        return Err(request);
                    },
                    Err(MalformedHeaderValue) => {
                        println("Bad header encountered. TODO: handle this better.");
                        // Now just ignore the header
                    },
                    Ok(header) => {
                        headers.insert(header);
                    },
                }
            }
            headers
        };

        Ok(ResponseReader {
            stream: stream,
            request: request,
            version: http_version,
            status: Status::from_code_and_reason(status_code, reason),
            headers: headers,
        })
    }
}

impl<S: Reader + Writer> Reader for ResponseReader<S> {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        self.stream.read(buf)
    }

    fn eof(&mut self) -> bool {
        self.stream.eof()
    }
}
<commit_msg>Convert a printfln to a log statement<commit_after>use std::rt::io::{Reader, Writer};
use std::rt::io::extensions::ReaderUtil;
use std::rt::io::{io_error, OtherIoError, IoError};
use client::request::RequestWriter;
use rfc2616::{CR, LF, SP};
use common::read_http_version;
use headers;
use status::Status;

use buffer::BufferedStream;
use server::request::{RequestBuffer};
use headers::{EndOfFile, EndOfHeaders, MalformedHeaderSyntax, MalformedHeaderValue};

struct ResponseReader<S> {
    priv stream: BufferedStream<S>,

    /// The request which this is a response to
    request: ~RequestWriter<S>,

    /// The HTTP version number; typically `(1, 1)` or, less commonly, `(1, 0)`.
    version: (uint, uint),

    /// The HTTP status indicated in the response.
    status: Status,

    /// The headers received in the response.
    headers: ~headers::response::HeaderCollection,
}

fn bad_response_err() -> IoError {
    // TODO: IoError isn't right
    IoError {
        kind: OtherIoError,
        desc: "Server returned malformed HTTP response",
        detail: None,
    }
}

impl<S: Reader + Writer> ResponseReader<S> {
    pub fn construct(mut stream: BufferedStream<S>, request: ~RequestWriter<S>)
            -> Result<ResponseReader<S>, ~RequestWriter<S>> {
        // TODO: raise condition at the points where Err is returned
        //let mut b = [0u8, ..4096];
        //let len = stream.read(b);
        //printfln!("%?", ::std::str::from_bytes(b.slice_to(len.unwrap())));
        let http_version = match read_http_version(&mut stream, SP) {
            Some(nums) => nums,
            None => {
                io_error::cond.raise(bad_response_err());
                return Err(request);
            }
        };

        // Read the status code
        let mut digits = 0u8;
        let mut status_code = 0u16;
        loop {
            if digits == 4u8 {
                // Status code must be three digits long
                io_error::cond.raise(bad_response_err());
                return Err(request);
            }
            match stream.read_byte() {
                Some(b) if b >= '0' as u8 && b <= '9' as u8 => {
                    status_code = status_code * 10 + b as u16 - '0' as u16;
                },
                Some(b) if b == SP => break,
                _ => {
                    io_error::cond.raise(bad_response_err());
                    return Err(request);
                }
            }
            digits += 1;
        }

        // Read the status reason
        let mut reason = ~"";
        loop {
            match stream.read_byte() {
                Some(b) if b == CR => {
                    if stream.read_byte() == Some(LF) {
                        break;
                    } else {
                        // Response-Line has CR without LF. Not yet resilient; TODO.
                        io_error::cond.raise(bad_response_err());
                        return Err(request);
                    }
                }
                Some(b) => {
                    reason.push_char(b as char);
                }
                None => {
                    io_error::cond.raise(bad_response_err());
                    return Err(request);
                }
            }
        }

        // Now we sneakily slip back to server::RequestBuffer to avoid code duplication. This is
        // temporary, honest!
        //
        // You see, read_header and read_header_line will be replaced, as will this. The code will
        // not be shared between them as they will have ultra-smart parsers (probably using Ragel)
        // to provide fast loading of standard headers, and the set of defined headers is distinct
        // between a request and response.
        let headers = {
            let mut buffer = RequestBuffer::new(&mut stream);
            let mut headers = ~headers::response::HeaderCollection::new();
            loop {
                let xxx = buffer.read_header::<headers::response::Header>();
                info!("header = %?", xxx);
                match xxx {
                //match buffer.read_header::<headers::response::Header>() {
                    Err(EndOfFile) => {
                        io_error::cond.raise(bad_response_err());
                        //fail!("server disconnected, no more response to receive :-(");
                        return Err(request);
                    },
                    Err(EndOfHeaders) => break,
                    Err(MalformedHeaderSyntax) => {
                        io_error::cond.raise(bad_response_err());
                        return Err(request);
                    },
                    Err(MalformedHeaderValue) => {
                        println("Bad header encountered. TODO: handle this better.");
                        // Now just ignore the header
                    },
                    Ok(header) => {
                        headers.insert(header);
                    },
                }
            }
            headers
        };

        Ok(ResponseReader {
            stream: stream,
            request: request,
            version: http_version,
            status: Status::from_code_and_reason(status_code, reason),
            headers: headers,
        })
    }
}

impl<S: Reader + Writer> Reader for ResponseReader<S> {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        self.stream.read(buf)
    }

    fn eof(&mut self) -> bool {
        self.stream.eof()
    }
}
<|endoftext|>