From f9994bff47a5de4c8ec33f943932f6c164e91a29 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Wed, 28 Dec 2022 18:15:44 -0800 Subject: [PATCH] fix: Broken long arg inference on conflicts Signed-off-by: Alex Saveau --- clap_builder/src/parser/parser.rs | 6 ++-- tests/builder/opts.rs | 56 ++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/clap_builder/src/parser/parser.rs b/clap_builder/src/parser/parser.rs index 09e8984391e..d2e198b2885 100644 --- a/clap_builder/src/parser/parser.rs +++ b/clap_builder/src/parser/parser.rs @@ -729,7 +729,7 @@ impl<'cmd> Parser<'cmd> { debug!("Parser::parse_long_arg: Found valid arg or flag '{arg}'"); Some((long_arg, arg)) } else if self.cmd.is_infer_long_args_set() { - self.cmd.get_arguments().find_map(|a| { + let mut iter = self.cmd.get_arguments().filter_map(|a| { if let Some(long) = a.get_long() { if long.starts_with(long_arg) { return Some((long, a)); @@ -738,7 +738,9 @@ impl<'cmd> Parser<'cmd> { a.aliases .iter() .find_map(|(alias, _)| alias.starts_with(long_arg).then(|| (alias.as_str(), a))) - }) + }); + + iter.next().filter(|_| iter.next().is_none()) } else { None }; diff --git a/tests/builder/opts.rs b/tests/builder/opts.rs index 25a9d271bb3..501bb469c3d 100644 --- a/tests/builder/opts.rs +++ b/tests/builder/opts.rs @@ -659,7 +659,7 @@ fn issue_2279() { } #[test] -fn infer_long_arg() { +fn infer_long_arg_pass() { let cmd = Command::new("test") .infer_long_args(true) .arg( @@ -716,3 +716,57 @@ fn infer_long_arg() { let matches = cmd.clone().try_get_matches_from(["test", "--a"]).unwrap(); assert!(*matches.get_one::("arg").expect("defaulted by clap")); } + +#[test] +fn infer_long_arg_pass_conflicts_exact_match() { + let cmd = Command::new("test") + .infer_long_args(true) + .arg(Arg::new("arg").long("arg").action(ArgAction::SetTrue)) + .arg(Arg::new("arg2").long("arg2").action(ArgAction::SetTrue)); + + let matches = cmd.clone().try_get_matches_from(["test", "--arg"]).unwrap(); + assert!(*matches.get_one::("arg").expect("defaulted by clap")); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--arg2"]) + .unwrap(); + assert!(*matches.get_one::("arg2").expect("defaulted by clap")); +} + +#[test] +fn infer_long_arg_pass_conflicting_aliases() { + let cmd = Command::new("test").infer_long_args(true).arg( + Arg::new("abc-123") + .long("abc-123") + .aliases(["a", "abc-xyz"]) + .action(ArgAction::SetTrue), + ); + + let matches = cmd.clone().try_get_matches_from(["test", "--ab"]).unwrap(); + assert!(*matches + .get_one::("abc-123") + .expect("defaulted by clap")); +} + +#[test] +fn infer_long_arg_fail_conflicts() { + let cmd = Command::new("test") + .infer_long_args(true) + .arg( + Arg::new("abc-123") + .long("abc-123") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("abc-xyz") + .long("abc-xyz") + .action(ArgAction::SetTrue), + ); + + let error = cmd + .clone() + .try_get_matches_from(["test", "--abc"]) + .unwrap_err(); + assert_eq!(error.kind(), ErrorKind::UnknownArgument); +}