]>
Commit | Line | Data |
---|---|---|
041b39d2 | 1 | use app::App; |
8bb4bdeb XL |
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))] | |
041b39d2 | 15 | pub fn did_you_mean<'a, T: ?Sized, I>(v: &str, possible_values: I) -> Option<&'a str> |
8bb4bdeb XL |
16 | where T: AsRef<str> + 'a, |
17 | I: IntoIterator<Item = &'a T> | |
18 | { | |
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 && | |
24 | (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence)) { | |
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"))] | |
041b39d2 | 35 | pub fn did_you_mean<'a, T: ?Sized, I>(_: &str, _: I) -> Option<&'a str> |
8bb4bdeb XL |
36 | where T: AsRef<str> + 'a, |
37 | I: IntoIterator<Item = &'a T> | |
38 | { | |
39 | None | |
40 | } | |
41 | ||
42 | /// Returns a suffix that can be empty, or is the standard 'did you mean' phrase | |
43 | #[cfg_attr(feature = "lints", allow(needless_lifetimes))] | |
041b39d2 | 44 | pub fn did_you_mean_flag_suffix<'z, T, I>(arg: &str, longs: I, subcommands: &'z [App]) |
8bb4bdeb XL |
45 | -> (String, Option<&'z str>) |
46 | where T: AsRef<str> + 'z, | |
47 | I: IntoIterator<Item = &'z T> | |
48 | { | |
041b39d2 | 49 | match did_you_mean(arg, longs) { |
8bb4bdeb | 50 | Some(candidate) => { |
041b39d2 XL |
51 | let suffix = format!("\n\tDid you mean {}{}?", Format::Good("--"), Format::Good(candidate)); |
52 | return (suffix, Some(candidate)) | |
53 | } | |
54 | None => { | |
55 | for subcommand in subcommands { | |
56 | let opts = subcommand.p.flags.iter().filter_map(|f| f.s.long).chain( | |
57 | subcommand.p.opts.iter().filter_map(|o| o.s.long)); | |
58 | ||
59 | if let Some(candidate) = did_you_mean(arg, opts) { | |
60 | let suffix = format!( | |
61 | "\n\tDid you mean to put '--{}' after the subcommand '{}'?", | |
62 | Format::Good(arg), | |
63 | Format::Good(candidate)); | |
64 | return (suffix, Some(candidate)); | |
8bb4bdeb | 65 | } |
8bb4bdeb | 66 | } |
8bb4bdeb | 67 | } |
8bb4bdeb | 68 | } |
041b39d2 | 69 | return (String::new(), None) |
8bb4bdeb XL |
70 | } |
71 | ||
041b39d2 XL |
72 | /// Returns a suffix that can be empty, or is the standard 'did you mean' phrase |
73 | pub fn did_you_mean_value_suffix<'z, T, I>(arg: &str, values: I) -> (String, Option<&'z str>) | |
74 | where T: AsRef<str> + 'z, | |
75 | I: IntoIterator<Item = &'z T> | |
76 | { | |
77 | match did_you_mean(arg, values) { | |
78 | Some(candidate) => { | |
79 | let suffix = format!("\n\tDid you mean '{}'?", Format::Good(candidate)); | |
80 | (suffix, Some(candidate)) | |
81 | } | |
82 | None => (String::new(), None), | |
83 | } | |
8bb4bdeb XL |
84 | } |
85 | ||
86 | #[cfg(all(test, features = "suggestions"))] | |
87 | mod test { | |
88 | use super::*; | |
89 | ||
90 | #[test] | |
91 | fn possible_values_match() { | |
92 | let p_vals = ["test", "possible", "values"]; | |
93 | assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test")); | |
94 | } | |
95 | ||
96 | #[test] | |
97 | fn possible_values_nomatch() { | |
98 | let p_vals = ["test", "possible", "values"]; | |
99 | assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none()); | |
100 | } | |
101 | ||
102 | #[test] | |
103 | fn suffix_long() { | |
104 | let p_vals = ["test", "possible", "values"]; | |
105 | let suffix = "\n\tDid you mean \'--test\'?"; | |
041b39d2 | 106 | assert_eq!(did_you_mean_flag_suffix("tst", p_vals.iter(), []), (suffix, Some("test"))); |
8bb4bdeb XL |
107 | } |
108 | ||
109 | #[test] | |
110 | fn suffix_enum() { | |
111 | let p_vals = ["test", "possible", "values"]; | |
112 | let suffix = "\n\tDid you mean \'test\'?"; | |
041b39d2 | 113 | assert_eq!(did_you_mean_value_suffix("tst", p_vals.iter()), (suffix, Some("test"))); |
8bb4bdeb XL |
114 | } |
115 | } |