• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #[cfg(feature = "suggestions")]
2 use std::cmp::Ordering;
3 
4 // Internal
5 use crate::builder::Command;
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<'a, 'help, I, T>( arg: &str, remaining_args: &[&str], longs: I, subcommands: impl IntoIterator<Item = &'a mut Command<'help>>, ) -> Option<(String, Option<String>)> where 'help: 'a, T: AsRef<str>, I: IntoIterator<Item = T>,36 pub(crate) fn did_you_mean_flag<'a, 'help, I, T>(
37     arg: &str,
38     remaining_args: &[&str],
39     longs: I,
40     subcommands: impl IntoIterator<Item = &'a mut Command<'help>>,
41 ) -> Option<(String, Option<String>)>
42 where
43     'help: 'a,
44     T: AsRef<str>,
45     I: IntoIterator<Item = T>,
46 {
47     use crate::mkeymap::KeyType;
48 
49     match did_you_mean(arg, longs).pop() {
50         Some(candidate) => Some((candidate, None)),
51         None => subcommands
52             .into_iter()
53             .filter_map(|subcommand| {
54                 subcommand._build_self();
55 
56                 let longs = subcommand.get_keymap().keys().filter_map(|a| {
57                     if let KeyType::Long(v) = a {
58                         Some(v.to_string_lossy().into_owned())
59                     } else {
60                         None
61                     }
62                 });
63 
64                 let subcommand_name = subcommand.get_name();
65 
66                 let candidate = did_you_mean(arg, longs).pop()?;
67                 let score = remaining_args.iter().position(|x| *x == subcommand_name)?;
68                 Some((score, (candidate, Some(subcommand_name.to_string()))))
69             })
70             .min_by_key(|(x, _)| *x)
71             .map(|(_, suggestion)| suggestion),
72     }
73 }
74 
75 #[cfg(all(test, features = "suggestions"))]
76 mod test {
77     use super::*;
78 
79     #[test]
possible_values_match()80     fn possible_values_match() {
81         let p_vals = ["test", "possible", "values"];
82         assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test"));
83     }
84 
85     #[test]
possible_values_match()86     fn possible_values_match() {
87         let p_vals = ["test", "temp"];
88         assert_eq!(did_you_mean("te", p_vals.iter()), Some("test"));
89     }
90 
91     #[test]
possible_values_nomatch()92     fn possible_values_nomatch() {
93         let p_vals = ["test", "possible", "values"];
94         assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none());
95     }
96 
97     #[test]
flag()98     fn flag() {
99         let p_vals = ["test", "possible", "values"];
100         assert_eq!(
101             did_you_mean_flag("tst", p_vals.iter(), []),
102             Some(("test", None))
103         );
104     }
105 }
106