From 1cc2deb29158e0e4e8b434e4ce26b3d819301a7d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 5 May 2015 17:23:51 +0200 Subject: [PATCH] feat(did-you-mean): for possible values There now is a single method which deals with formatting the 'did-you-mean' message, supporting different styles to cater all the various needs that have arisen thus far, with enough potential to be easily extended in future through a little helper-enumeration whose variants can possibly take values. *NOTE*: We might still want to have a look at where the did-you-mean message should be located for best effect. Related to #103 --- clap-tests/run_tests.py | 7 ++++++ src/app.rs | 54 +++++++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/clap-tests/run_tests.py b/clap-tests/run_tests.py index 59a01b83755..04f36d1efab 100755 --- a/clap-tests/run_tests.py +++ b/clap-tests/run_tests.py @@ -45,6 +45,12 @@ \tclaptests For more information try --help''' +_pv_dym_usage = '''"slo" isn't a valid value for '--Option ' +\t[valid values: fast slow]. Did you mean "slow" ? +USAGE: +\tclaptests --Option +For more information try --help''' + _excluded = '''The argument '--flag' cannot be used with '-F' USAGE: \tclaptests [positional2] -F --long-option-2 @@ -224,6 +230,7 @@ 'mult_valsmo x1: ': ['{} --multvalsmo some other'.format(_bin), _exact], 'F2(ss),O(s),P: ': ['{} value -f -f -o some'.format(_bin), _f2op], 'arg dym: ': ['{} --optio=foo'.format(_bin), _arg_dym_usage], + 'pv dym: ': ['{} --Option slo'.format(_bin), _pv_dym_usage], 'O2(ll)P: ': ['{} value --option some --option other'.format(_bin), _o2p], 'O2(l=l=)P: ': ['{} value --option=some --option=other'.format(_bin), _o2p], 'O2(ss)P: ': ['{} value -o some -o other'.format(_bin), _o2p], diff --git a/src/app.rs b/src/app.rs index 79e152a9a27..223c2365297 100644 --- a/src/app.rs +++ b/src/app.rs @@ -17,9 +17,9 @@ use strsim; /// the passed in value `v` with a certain confidence. /// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield /// `Some("foo")`, whereas "blark" would yield `None`. -fn did_you_mean<'a, I, T>(v: &str, possible_values: I) -> Option<&'a str> +fn did_you_mean<'a, T, I>(v: &str, possible_values: I) -> Option<&'a str> where T: AsRef + 'a, - I: IntoIterator{ + I: IntoIterator { let mut candidate: Option<(f64, &str)> = None; for pv in possible_values.into_iter() { @@ -35,6 +35,14 @@ fn did_you_mean<'a, I, T>(v: &str, possible_values: I) -> Option<&'a str> } } +/// A helper to determine message formatting +enum DidYouMeanMessageStyle { + /// Suggested value is a long flag + LongFlag, + /// Suggested value is one of various possible values + EnumValue, +} + /// Used to create a representation of a command line program and all possible command line /// arguments for parsing at runtime. /// @@ -1207,16 +1215,43 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } + /// Returns a suffix that can be empty, or is the standard 'did you mean phrase + fn did_you_mean_suffix<'z, T, I>(arg: &str, values: I, style: DidYouMeanMessageStyle) + -> String + where T: AsRef + 'z, + I: IntoIterator { + match did_you_mean(arg, values) { + Some(candidate) => { + let mut suffix = ". Did you mean ".to_string(); + match style { + DidYouMeanMessageStyle::LongFlag => suffix.push_str("--"), + DidYouMeanMessageStyle::EnumValue => suffix.push('"'), + } + suffix.push_str(candidate); + if let DidYouMeanMessageStyle::EnumValue = style { + suffix.push('"'); + } + suffix.push_str(" ?"); + suffix + }, + None => String::new(), + } + } + fn possible_values_error(&self, arg: &str, opt: &str, p_vals: &BTreeSet<&str>, matches: &ArgMatches<'ar, 'ar>) { - self.report_error(format!("\"{}\" isn't a valid value for '{}'{}", + let suffix = App::did_you_mean_suffix(arg, p_vals.iter(), + DidYouMeanMessageStyle::EnumValue); + + self.report_error(format!("\"{}\" isn't a valid value for '{}'{}{}", arg, opt, format!("\n\t[valid values:{}]", p_vals.iter() .fold(String::new(), |acc, name| { acc + &format!(" {}",name)[..] - }))), + })), + suffix), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -1775,21 +1810,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return None; } - let suffix = - match did_you_mean(arg, self.opts.values() + let suffix = App::did_you_mean_suffix(arg, self.opts.values() .filter_map(|v| if let Some(ref l) = v.long { Some(l) } else { None } - )) { - Some(candidate_flag) => { - format!(". Did you mean --{} ?", candidate_flag) - }, - None => String::new() - }; - + ), DidYouMeanMessageStyle::LongFlag); self.report_error(format!("The argument --{} isn't valid{}", arg, suffix), true, true,