Skip to content

Commit

Permalink
feat(did-you-mean): for possible values
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Byron authored and kbknapp committed May 5, 2015
1 parent 627ec10 commit 1cc2deb
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 13 deletions.
7 changes: 7 additions & 0 deletions clap-tests/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
\tclaptests
For more information try --help'''

_pv_dym_usage = '''"slo" isn't a valid value for '--Option <option3>'
\t[valid values: fast slow]. Did you mean "slow" ?
USAGE:
\tclaptests --Option <option3>
For more information try --help'''

_excluded = '''The argument '--flag' cannot be used with '-F'
USAGE:
\tclaptests [positional2] -F --long-option-2 <option2>
Expand Down Expand Up @@ -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],
Expand Down
54 changes: 41 additions & 13 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<str> + 'a,
I: IntoIterator<Item=&'a T>{
I: IntoIterator<Item=&'a T> {

let mut candidate: Option<(f64, &str)> = None;
for pv in possible_values.into_iter() {
Expand All @@ -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.
///
Expand Down Expand Up @@ -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<str> + 'z,
I: IntoIterator<Item=&'z T> {
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()));
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 1cc2deb

Please sign in to comment.