Skip to content

Commit

Permalink
feat(error): Show possible values when none are supplied
Browse files Browse the repository at this point in the history
This is inspired by cargo which allows you to run `cargo test --test`
and it will list the possible tests (obviously we can't support that atm
because that requires a lot of runtime processing).  When we do have a
static list of possible values, we can at least show those.

Fixes clap-rs#3320
  • Loading branch information
epage committed Feb 2, 2022
1 parent d4cfcee commit 9d482d0
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 1 deletion.
26 changes: 25 additions & 1 deletion src/parse/errors.rs
Expand Up @@ -605,13 +605,37 @@ impl Error {
Self::for_app(app, c, ErrorKind::ArgumentConflict, others)
}

pub(crate) fn empty_value(app: &App, arg: &Arg, usage: String) -> Self {
pub(crate) fn empty_value(app: &App, good_vals: &[&str], arg: &Arg, usage: String) -> Self {
let mut c = Colorizer::new(true, app.get_color());
let arg = arg.to_string();

start_error(&mut c, "The argument '");
c.warning(&*arg);
c.none("' requires a value but none was supplied");
if !good_vals.is_empty() {
let good_vals: Vec<String> = good_vals
.iter()
.map(|&v| {
if v.contains(char::is_whitespace) {
format!("{:?}", v)
} else {
v.to_owned()
}
})
.collect();
c.none("\n\t[possible values: ");

if let Some((last, elements)) = good_vals.split_last() {
for v in elements {
c.good(v);
c.none(", ");
}

c.good(last);
}

c.none("]");
}
put_usage(&mut c, usage);
try_help(app, &mut c);

Expand Down
12 changes: 12 additions & 0 deletions src/parse/validator.rs
Expand Up @@ -49,6 +49,10 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
if should_err {
return Err(Error::empty_value(
self.p.app,
&o.possible_vals
.iter()
.filter_map(PossibleValue::get_visible_name)
.collect::<Vec<_>>(),
o,
Usage::new(self.p.app, &self.p.required).create_usage_with_title(&[]),
));
Expand Down Expand Up @@ -133,6 +137,10 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
debug!("Validator::validate_arg_values: illegal empty val found");
return Err(Error::empty_value(
self.p.app,
&arg.possible_vals
.iter()
.filter_map(PossibleValue::get_visible_name)
.collect::<Vec<_>>(),
arg,
Usage::new(self.p.app, &self.p.required).create_usage_with_title(&[]),
));
Expand Down Expand Up @@ -407,6 +415,10 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
if a.is_set(ArgSettings::TakesValue) && !min_vals_zero && ma.all_val_groups_empty() {
return Err(Error::empty_value(
self.p.app,
&a.possible_vals
.iter()
.filter_map(PossibleValue::get_visible_name)
.collect::<Vec<_>>(),
a,
Usage::new(self.p.app, &self.p.required).create_usage_with_title(&[]),
));
Expand Down
27 changes: 27 additions & 0 deletions tests/builder/possible_values.rs
Expand Up @@ -265,6 +265,33 @@ fn escaped_possible_values_output() {
));
}

#[test]
fn missing_possible_value_error() {
assert!(utils::compare_output(
App::new("test").arg(
Arg::new("option")
.short('O')
.possible_value("slow")
.possible_value(PossibleValue::new("fast").alias("fost"))
.possible_value(PossibleValue::new("ludicrous speed"))
.possible_value(PossibleValue::new("forbidden speed").hide(true))
),
"clap-test -O",
MISSING_PV_ERROR,
true
));
}

static MISSING_PV_ERROR: &str =
"error: The argument '-O <option>' requires a value but none was supplied
\t[possible values: slow, fast, \"ludicrous speed\"]
USAGE:
clap-test [OPTIONS]
For more information try --help
";

#[test]
fn alias() {
let m = App::new("pv")
Expand Down

0 comments on commit 9d482d0

Please sign in to comment.