1 use app::App;
2 // Third Party
3 #[cfg(feature = "suggestions")]
4 use strsim;
5
6 // Internal
7 use fmt::Format;
8
9 /// Produces a string from a given list of possible values which is similar to
10 /// the passed in value `v` with a certain confidence.
11 /// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield
12 /// `Some("foo")`, whereas "blark" would yield `None`.
13 #[cfg(feature = "suggestions")]
14 #[cfg_attr(feature = "lints", allow(needless_lifetimes))]
did_you_mean<'a, T: ?Sized, I>(v: &str, possible_values: I) -> Option<&'a str> where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,15 pub fn did_you_mean<'a, T: ?Sized, I>(v: &str, possible_values: I) -> Option<&'a str>
16 where
17 T: AsRef<str> + 'a,
18 I: IntoIterator<Item = &'a T>,
19 {
20 let mut candidate: Option<(f64, &str)> = None;
21 for pv in possible_values {
22 let confidence = strsim::jaro_winkler(v, pv.as_ref());
23 if confidence > 0.8 && (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence))
24 {
25 candidate = Some((confidence, pv.as_ref()));
26 }
27 }
28 match candidate {
29 None => None,
30 Some((_, candidate)) => Some(candidate),
31 }
32 }
33
34 #[cfg(not(feature = "suggestions"))]
did_you_mean<'a, T: ?Sized, I>(_: &str, _: I) -> Option<&'a str> where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,35 pub fn did_you_mean<'a, T: ?Sized, I>(_: &str, _: I) -> Option<&'a str>
36 where
37 T: AsRef<str> + 'a,
38 I: IntoIterator<Item = &'a T>,
39 {
40 None
41 }
42
43 /// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
44 #[cfg_attr(feature = "lints", allow(needless_lifetimes))]
did_you_mean_flag_suffix<'z, T, I>( arg: &str, args_rest: &'z [&str], longs: I, subcommands: &'z [App], ) -> (String, Option<&'z str>) where T: AsRef<str> + 'z, I: IntoIterator<Item = &'z T>,45 pub fn did_you_mean_flag_suffix<'z, T, I>(
46 arg: &str,
47 args_rest: &'z [&str],
48 longs: I,
49 subcommands: &'z [App],
50 ) -> (String, Option<&'z str>)
51 where
52 T: AsRef<str> + 'z,
53 I: IntoIterator<Item = &'z T>,
54 {
55 if let Some(candidate) = did_you_mean(arg, longs) {
56 let suffix = format!(
57 "\n\tDid you mean {}{}?",
58 Format::Good("--"),
59 Format::Good(candidate)
60 );
61 return (suffix, Some(candidate));
62 }
63
64 subcommands
65 .into_iter()
66 .filter_map(|subcommand| {
67 let opts = subcommand
68 .p
69 .flags
70 .iter()
71 .filter_map(|f| f.s.long)
72 .chain(subcommand.p.opts.iter().filter_map(|o| o.s.long));
73
74 let candidate = match did_you_mean(arg, opts) {
75 Some(candidate) => candidate,
76 None => return None,
77 };
78 let score = match args_rest.iter().position(|x| *x == subcommand.get_name()) {
79 Some(score) => score,
80 None => return None,
81 };
82
83 let suffix = format!(
84 "\n\tDid you mean to put '{}{}' after the subcommand '{}'?",
85 Format::Good("--"),
86 Format::Good(candidate),
87 Format::Good(subcommand.get_name())
88 );
89
90 Some((score, (suffix, Some(candidate))))
91 })
92 .min_by_key(|&(score, _)| score)
93 .map(|(_, suggestion)| suggestion)
94 .unwrap_or_else(|| (String::new(), None))
95 }
96
97 /// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
did_you_mean_value_suffix<'z, T, I>(arg: &str, values: I) -> (String, Option<&'z str>) where T: AsRef<str> + 'z, I: IntoIterator<Item = &'z T>,98 pub fn did_you_mean_value_suffix<'z, T, I>(arg: &str, values: I) -> (String, Option<&'z str>)
99 where
100 T: AsRef<str> + 'z,
101 I: IntoIterator<Item = &'z T>,
102 {
103 match did_you_mean(arg, values) {
104 Some(candidate) => {
105 let suffix = format!("\n\tDid you mean '{}'?", Format::Good(candidate));
106 (suffix, Some(candidate))
107 }
108 None => (String::new(), None),
109 }
110 }
111
112 #[cfg(all(test, features = "suggestions"))]
113 mod test {
114 use super::*;
115
116 #[test]
possible_values_match()117 fn possible_values_match() {
118 let p_vals = ["test", "possible", "values"];
119 assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test"));
120 }
121
122 #[test]
possible_values_nomatch()123 fn possible_values_nomatch() {
124 let p_vals = ["test", "possible", "values"];
125 assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none());
126 }
127
128 #[test]
suffix_long()129 fn suffix_long() {
130 let p_vals = ["test", "possible", "values"];
131 let suffix = "\n\tDid you mean \'--test\'?";
132 assert_eq!(
133 did_you_mean_flag_suffix("tst", p_vals.iter(), []),
134 (suffix, Some("test"))
135 );
136 }
137
138 #[test]
suffix_enum()139 fn suffix_enum() {
140 let p_vals = ["test", "possible", "values"];
141 let suffix = "\n\tDid you mean \'test\'?";
142 assert_eq!(
143 did_you_mean_value_suffix("tst", p_vals.iter()),
144 (suffix, Some("test"))
145 );
146 }
147 }
148