    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105

    #[cfg(feature = "suggestions")]
    use std::cmp::Ordering;

    // Internal
    use crate::builder::Command;

    /// Produces multiple strings from a given list of possible values which are similar
    /// to the passed in value `v` within a certain confidence by least confidence.
    /// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield
    /// `Some("foo")`, whereas "blark" would yield `None`.
    #[cfg(feature = "suggestions")]
    pub(crate) fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
    where
        T: AsRef<str>,
        I: IntoIterator<Item = T>,
    {
        let mut candidates: Vec<(f64, String)> = possible_values
            .into_iter()
            .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned()))
            .filter(|(confidence, _)| *confidence > 0.8)
            .collect();
        candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
        candidates.into_iter().map(|(_, pv)| pv).collect()
    }

    #[cfg(not(feature = "suggestions"))]
    pub(crate) fn did_you_mean<T, I>(_: &str, _: I) -> Vec<String>
    where
        T: AsRef<str>,
        I: IntoIterator<Item = T>,
    {
        Vec::new()
    }

    /// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
    pub(crate) fn did_you_mean_flag<'a, 'help, I, T>(
        arg: &str,
        remaining_args: &[&std::ffi::OsStr],
        longs: I,
        subcommands: impl IntoIterator<Item = &'a mut Command>,
    ) -> Option<(String, Option<String>)>
    where
        'help: 'a,
        T: AsRef<str>,
        I: IntoIterator<Item = T>,
    {
        use crate::mkeymap::KeyType;

        match did_you_mean(arg, longs).pop() {
            Some(candidate) => Some((candidate, None)),
            None => subcommands
                .into_iter()
                .filter_map(|subcommand| {
                    subcommand._build_self(false);

                    let longs = subcommand.get_keymap().keys().filter_map(|a| {
                        if let KeyType::Long(v) = a {
                            Some(v.to_string_lossy().into_owned())
                        } else {
                            None
                        }
                    });

                    let subcommand_name = subcommand.get_name();

                    let candidate = some!(did_you_mean(arg, longs).pop());
                    let score = some!(remaining_args.iter().position(|x| subcommand_name == *x));
                    Some((score, (candidate, Some(subcommand_name.to_string()))))
                })
                .min_by_key(|(x, _)| *x)
                .map(|(_, suggestion)| suggestion),
        }
    }

    #[cfg(all(test, features = "suggestions"))]
    mod test {
        use super::*;

        #[test]
        fn possible_values_match() {
            let p_vals = ["test", "possible", "values"];
            assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test"));
        }

        #[test]
        fn possible_values_match() {
            let p_vals = ["test", "temp"];
            assert_eq!(did_you_mean("te", p_vals.iter()), Some("test"));
        }

        #[test]
        fn possible_values_nomatch() {
            let p_vals = ["test", "possible", "values"];
            assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none());
        }

        #[test]
        fn flag() {
            let p_vals = ["test", "possible", "values"];
            assert_eq!(
                did_you_mean_flag("tst", p_vals.iter(), []),
                Some(("test", None))
            );
        }
    }