Skip to content

Commit d5bea65

Browse files
authored
Merge pull request #4924 from epage/term2
fix(parser): Allow multiple value terminated positionals
2 parents 65d44aa + e1db168 commit d5bea65

File tree

3 files changed

+66
-19
lines changed

3 files changed

+66
-19
lines changed

clap_builder/src/builder/debug_asserts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ fn _verify_positionals(cmd: &Command) -> bool {
603603
.get_positionals()
604604
.filter(|p| {
605605
p.is_multiple_values_set()
606+
&& p.get_value_terminator().is_none()
606607
&& !p.get_num_args().expect(INTERNAL_ERROR_MSG).is_fixed()
607608
})
608609
.count();

clap_builder/src/parser/parser.rs

+20-19
Original file line numberDiff line numberDiff line change
@@ -372,26 +372,27 @@ impl<'cmd> Parser<'cmd> {
372372
if matcher.pending_arg_id() != Some(arg.get_id()) || !arg.is_multiple_values_set() {
373373
ok!(self.resolve_pending(matcher));
374374
}
375-
if let Some(_parse_result) = self.check_terminator(arg, arg_os.to_value_os()) {
376-
debug!(
377-
"Parser::get_matches_with: ignoring terminator result {_parse_result:?}"
378-
);
379-
} else {
380-
let arg_values = matcher.pending_values_mut(
381-
arg.get_id(),
382-
Some(Identifier::Index),
383-
trailing_values,
384-
);
385-
arg_values.push(arg_os.to_value_os().to_owned());
386-
}
375+
parse_state =
376+
if let Some(parse_result) = self.check_terminator(arg, arg_os.to_value_os()) {
377+
debug_assert_eq!(parse_result, ParseResult::ValuesDone);
378+
pos_counter += 1;
379+
ParseState::ValuesDone
380+
} else {
381+
let arg_values = matcher.pending_values_mut(
382+
arg.get_id(),
383+
Some(Identifier::Index),
384+
trailing_values,
385+
);
386+
arg_values.push(arg_os.to_value_os().to_owned());
387387

388-
// Only increment the positional counter if it doesn't allow multiples
389-
if !arg.is_multiple() {
390-
pos_counter += 1;
391-
parse_state = ParseState::ValuesDone;
392-
} else {
393-
parse_state = ParseState::Pos(arg.get_id().clone());
394-
}
388+
// Only increment the positional counter if it doesn't allow multiples
389+
if !arg.is_multiple() {
390+
pos_counter += 1;
391+
ParseState::ValuesDone
392+
} else {
393+
ParseState::Pos(arg.get_id().clone())
394+
}
395+
};
395396
valid_arg_found = true;
396397
} else if let Some(external_parser) =
397398
self.cmd.get_external_subcommand_value_parser().cloned()

tests/builder/multiple_values.rs

+45
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,51 @@ fn multiple_vals_with_hyphen() {
14111411
);
14121412
}
14131413

1414+
#[test]
1415+
fn multiple_positional_multiple_values() {
1416+
let res = Command::new("do")
1417+
.arg(
1418+
Arg::new("cmd1")
1419+
.action(ArgAction::Set)
1420+
.num_args(1..)
1421+
.allow_hyphen_values(true)
1422+
.value_terminator(";"),
1423+
)
1424+
.arg(
1425+
Arg::new("cmd2")
1426+
.action(ArgAction::Set)
1427+
.num_args(1..)
1428+
.allow_hyphen_values(true)
1429+
.value_terminator(";"),
1430+
)
1431+
.try_get_matches_from(vec![
1432+
"do",
1433+
"find",
1434+
"-type",
1435+
"f",
1436+
"-name",
1437+
"special",
1438+
";",
1439+
"/home/clap",
1440+
"foo",
1441+
]);
1442+
assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());
1443+
1444+
let m = res.unwrap();
1445+
let cmd1: Vec<_> = m
1446+
.get_many::<String>("cmd1")
1447+
.unwrap()
1448+
.map(|v| v.as_str())
1449+
.collect();
1450+
assert_eq!(&cmd1, &["find", "-type", "f", "-name", "special"]);
1451+
let cmd2: Vec<_> = m
1452+
.get_many::<String>("cmd2")
1453+
.unwrap()
1454+
.map(|v| v.as_str())
1455+
.collect();
1456+
assert_eq!(&cmd2, &["/home/clap", "foo"]);
1457+
}
1458+
14141459
#[test]
14151460
fn issue_1480_max_values_consumes_extra_arg_1() {
14161461
let res = Command::new("prog")

0 commit comments

Comments
 (0)