Skip to content

Commit 79d696f

Browse files
authored
Merge pull request #5813 from epage/ignore
fix(parser): Fill in defaults on ignored error
2 parents ba4745d + 479df35 commit 79d696f

File tree

2 files changed

+86
-27
lines changed

2 files changed

+86
-27
lines changed

clap_builder/src/parser/parser.rs

+30-11
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,36 @@ impl<'cmd> Parser<'cmd> {
5050
&mut self,
5151
matcher: &mut ArgMatcher,
5252
raw_args: &mut clap_lex::RawArgs,
53-
mut args_cursor: clap_lex::ArgCursor,
53+
args_cursor: clap_lex::ArgCursor,
5454
) -> ClapResult<()> {
5555
debug!("Parser::get_matches_with");
56+
57+
ok!(self.parse(matcher, raw_args, args_cursor).map_err(|err| {
58+
if self.cmd.is_ignore_errors_set() {
59+
#[cfg(feature = "env")]
60+
let _ = self.add_env(matcher);
61+
let _ = self.add_defaults(matcher);
62+
}
63+
err
64+
}));
65+
ok!(self.resolve_pending(matcher));
66+
67+
#[cfg(feature = "env")]
68+
ok!(self.add_env(matcher));
69+
ok!(self.add_defaults(matcher));
70+
71+
Validator::new(self.cmd).validate(matcher)
72+
}
73+
74+
// The actual parsing function
75+
#[allow(clippy::cognitive_complexity)]
76+
pub(crate) fn parse(
77+
&mut self,
78+
matcher: &mut ArgMatcher,
79+
raw_args: &mut clap_lex::RawArgs,
80+
mut args_cursor: clap_lex::ArgCursor,
81+
) -> ClapResult<()> {
82+
debug!("Parser::parse");
5683
// Verify all positional assertions pass
5784

5885
let mut subcmd_name: Option<String> = None;
@@ -436,11 +463,7 @@ impl<'cmd> Parser<'cmd> {
436463
matches: sc_m.into_inner(),
437464
});
438465

439-
ok!(self.resolve_pending(matcher));
440-
#[cfg(feature = "env")]
441-
ok!(self.add_env(matcher));
442-
ok!(self.add_defaults(matcher));
443-
return Validator::new(self.cmd).validate(matcher);
466+
return Ok(());
444467
} else {
445468
// Start error processing
446469
let _ = self.resolve_pending(matcher);
@@ -474,11 +497,7 @@ impl<'cmd> Parser<'cmd> {
474497
ok!(self.parse_subcommand(&sc_name, matcher, raw_args, args_cursor, keep_state));
475498
}
476499

477-
ok!(self.resolve_pending(matcher));
478-
#[cfg(feature = "env")]
479-
ok!(self.add_env(matcher));
480-
ok!(self.add_defaults(matcher));
481-
Validator::new(self.cmd).validate(matcher)
500+
Ok(())
482501
}
483502

484503
fn match_arg_error(

tests/builder/ignore_errors.rs

+56-16
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,52 @@ use super::utils;
44

55
#[test]
66
fn single_short_arg_without_value() {
7-
let cmd = Command::new("cmd").ignore_errors(true).arg(arg!(
8-
-c --config [FILE] "Sets a custom config file"
9-
));
7+
let cmd = Command::new("cmd")
8+
.ignore_errors(true)
9+
.arg(arg!(
10+
-c --config <FILE> "Sets a custom config file"
11+
))
12+
.arg(arg!(--"unset-flag"));
1013

1114
let r = cmd.try_get_matches_from(vec!["cmd", "-c" /* missing: , "config file" */]);
1215

1316
assert!(r.is_ok(), "unexpected error: {r:?}");
1417
let m = r.unwrap();
1518
assert!(m.contains_id("config"));
19+
assert_eq!(m.get_one::<String>("config").cloned(), None);
20+
assert_eq!(m.get_one::<bool>("unset-flag").copied(), Some(false));
1621
}
1722

1823
#[test]
1924
fn single_long_arg_without_value() {
20-
let cmd = Command::new("cmd").ignore_errors(true).arg(arg!(
21-
-c --config [FILE] "Sets a custom config file"
22-
));
25+
let cmd = Command::new("cmd")
26+
.ignore_errors(true)
27+
.arg(arg!(
28+
-c --config <FILE> "Sets a custom config file"
29+
))
30+
.arg(arg!(--"unset-flag"));
2331

2432
let r = cmd.try_get_matches_from(vec!["cmd", "--config" /* missing: , "config file" */]);
2533

2634
assert!(r.is_ok(), "unexpected error: {r:?}");
2735
let m = r.unwrap();
2836
assert!(m.contains_id("config"));
37+
assert_eq!(m.get_one::<String>("config").cloned(), None);
38+
assert_eq!(m.get_one::<bool>("unset-flag").copied(), Some(false));
2939
}
3040

3141
#[test]
3242
fn multiple_args_and_final_arg_without_value() {
3343
let cmd = Command::new("cmd")
3444
.ignore_errors(true)
3545
.arg(arg!(
36-
-c --config [FILE] "Sets a custom config file"
46+
-c --config <FILE> "Sets a custom config file"
3747
))
3848
.arg(arg!(
39-
-x --stuff [FILE] "Sets a custom stuff file"
49+
-x --stuff <FILE> "Sets a custom stuff file"
4050
))
41-
.arg(arg!(f: -f "Flag").action(ArgAction::SetTrue));
51+
.arg(arg!(f: -f "Flag").action(ArgAction::SetTrue))
52+
.arg(arg!(--"unset-flag"));
4253

4354
let r = cmd.try_get_matches_from(vec![
4455
"cmd", "-c", "file", "-f", "-x", /* missing: , "some stuff" */
@@ -50,21 +61,23 @@ fn multiple_args_and_final_arg_without_value() {
5061
m.get_one::<String>("config").map(|v| v.as_str()),
5162
Some("file")
5263
);
53-
assert!(*m.get_one::<bool>("f").expect("defaulted by clap"));
64+
assert_eq!(m.get_one::<bool>("f").copied(), Some(true));
5465
assert_eq!(m.get_one::<String>("stuff").map(|v| v.as_str()), None);
66+
assert_eq!(m.get_one::<bool>("unset-flag").copied(), Some(false));
5567
}
5668

5769
#[test]
5870
fn multiple_args_and_intermittent_arg_without_value() {
5971
let cmd = Command::new("cmd")
6072
.ignore_errors(true)
6173
.arg(arg!(
62-
-c --config[FILE] "Sets a custom config file"
74+
-c --config <FILE> "Sets a custom config file"
6375
))
6476
.arg(arg!(
65-
-x --stuff[FILE] "Sets a custom stuff file"
77+
-x --stuff <FILE> "Sets a custom stuff file"
6678
))
67-
.arg(arg!(f: -f "Flag").action(ArgAction::SetTrue));
79+
.arg(arg!(f: -f "Flag").action(ArgAction::SetTrue))
80+
.arg(arg!(--"unset-flag"));
6881

6982
let r = cmd.try_get_matches_from(vec![
7083
"cmd", "-x", /* missing: ,"some stuff" */
@@ -77,8 +90,30 @@ fn multiple_args_and_intermittent_arg_without_value() {
7790
m.get_one::<String>("config").map(|v| v.as_str()),
7891
Some("file")
7992
);
80-
assert!(*m.get_one::<bool>("f").expect("defaulted by clap"));
93+
assert_eq!(m.get_one::<bool>("f").copied(), Some(true));
8194
assert_eq!(m.get_one::<String>("stuff").map(|v| v.as_str()), None);
95+
assert_eq!(m.get_one::<bool>("unset-flag").copied(), Some(false));
96+
}
97+
98+
#[test]
99+
fn unexpected_argument() {
100+
let cmd = Command::new("cmd")
101+
.ignore_errors(true)
102+
.arg(arg!(
103+
-c --config [FILE] "Sets a custom config file"
104+
))
105+
.arg(arg!(--"unset-flag"));
106+
107+
let r = cmd.try_get_matches_from(vec!["cmd", "-c", "config file", "unexpected"]);
108+
109+
assert!(r.is_ok(), "unexpected error: {r:?}");
110+
let m = r.unwrap();
111+
assert!(m.contains_id("config"));
112+
assert_eq!(
113+
m.get_one::<String>("config").cloned(),
114+
Some("config file".to_owned())
115+
);
116+
assert_eq!(m.get_one::<bool>("unset-flag").copied(), Some(false));
82117
}
83118

84119
#[test]
@@ -100,9 +135,11 @@ fn subcommand() {
100135
.long("stuff")
101136
.action(ArgAction::Set)
102137
.help("stuf value"),
103-
),
138+
)
139+
.arg(arg!(--"unset-flag")),
104140
)
105-
.arg(Arg::new("other").long("other"));
141+
.arg(Arg::new("other").long("other"))
142+
.arg(arg!(--"unset-flag"));
106143

107144
let m = cmd
108145
.try_get_matches_from(vec![
@@ -125,6 +162,9 @@ fn subcommand() {
125162
sub_m.get_one::<String>("stuff").map(|v| v.as_str()),
126163
Some("some other val")
127164
);
165+
assert_eq!(sub_m.get_one::<bool>("unset-flag").copied(), Some(false));
166+
167+
assert_eq!(m.get_one::<bool>("unset-flag").copied(), Some(false));
128168
}
129169

130170
#[test]

0 commit comments

Comments
 (0)