1 #[cfg(feature = "suggestions")]
2 use std::cmp::Ordering;
3 
4 // Internal
5 use crate::build::App;
6 
7 /// Produces multiple strings from a given list of possible values which are similar
8 /// to the passed in value `v` within a certain confidence by least confidence.
9 /// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield
10 /// `Some("foo")`, whereas "blark" would yield `None`.
11 #[cfg(feature = "suggestions")]
did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String> where T: AsRef<str>, I: IntoIterator<Item = T>,12 pub(crate) fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
13 where
14     T: AsRef<str>,
15     I: IntoIterator<Item = T>,
16 {
17     let mut candidates: Vec<(f64, String)> = possible_values
18         .into_iter()
19         .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned()))
20         .filter(|(confidence, _)| *confidence > 0.8)
21         .collect();
22     candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
23     candidates.into_iter().map(|(_, pv)| pv).collect()
24 }
25 
26 #[cfg(not(feature = "suggestions"))]
did_you_mean<T, I>(_: &str, _: I) -> Vec<String> where T: AsRef<str>, I: IntoIterator<Item = T>,27 pub(crate) fn did_you_mean<T, I>(_: &str, _: I) -> Vec<String>
28 where
29     T: AsRef<str>,
30     I: IntoIterator<Item = T>,
31 {
32     Vec::new()
33 }
34 
35 /// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
did_you_mean_flag<I, T>( arg: &str, longs: I, subcommands: &mut [App], ) -> Option<(String, Option<String>)> where T: AsRef<str>, I: IntoIterator<Item = T>,36 pub(crate) fn did_you_mean_flag<I, T>(
37     arg: &str,
38     longs: I,
39     subcommands: &mut [App],
40 ) -> Option<(String, Option<String>)>
41 where
42     T: AsRef<str>,
43     I: IntoIterator<Item = T>,
44 {
45     use crate::mkeymap::KeyType;
46 
47     match did_you_mean(arg, longs).pop() {
48         Some(candidate) => Some((candidate, None)),
49 
50         None => {
51             for subcommand in subcommands {
52                 subcommand._build();
53 
54                 let longs = subcommand.args.keys.iter().map(|x| &x.key).filter_map(|a| {
55                     if let KeyType::Long(v) = a {
56                         Some(v.to_string_lossy().into_owned())
57                     } else {
58                         None
59                     }
60                 });
61 
62                 if let Some(candidate) = did_you_mean(arg, longs).pop() {
63                     return Some((candidate, Some(subcommand.get_name().to_string())));
64                 }
65             }
66             None
67         }
68     }
69 }
70 
71 #[cfg(all(test, features = "suggestions"))]
72 mod test {
73     use super::*;
74 
75     #[test]
possible_values_match()76     fn possible_values_match() {
77         let p_vals = ["test", "possible", "values"];
78         assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test"));
79     }
80 
81     #[test]
possible_values_match()82     fn possible_values_match() {
83         let p_vals = ["test", "temp"];
84         assert_eq!(did_you_mean("te", p_vals.iter()), Some("test"));
85     }
86 
87     #[test]
possible_values_nomatch()88     fn possible_values_nomatch() {
89         let p_vals = ["test", "possible", "values"];
90         assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none());
91     }
92 
93     #[test]
flag()94     fn flag() {
95         let p_vals = ["test", "possible", "values"];
96         assert_eq!(
97             did_you_mean_flag("tst", p_vals.iter(), []),
98             Some(("test", None))
99         );
100     }
101 }
102