Skip to content

Commit

Permalink
fix: push indices for optional values
Browse files Browse the repository at this point in the history
When an argument takes an optional value (e.g. `.num_args(0..=1)`), make sure an index is pushed:
* when a value is specified, for each value
* when no value is specified, for the option, similar to a flag.

Note: this means that a MatchedArg's `num_vals` no longer necessarily matches the number of indices.

Fixes clap-rs#2419
  • Loading branch information
fabianfreyer committed May 15, 2023
1 parent 3fa7b8f commit 260fe0e
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 1 deletion.
2 changes: 1 addition & 1 deletion clap_builder/src/parser/matches/arg_matches.rs
Expand Up @@ -834,7 +834,7 @@ impl ArgMatches {
let arg = some!(self.get_arg(id));
let i = Indices {
iter: arg.indices(),
len: arg.num_vals(),
len: arg.num_indices(),
};
Some(i)
}
Expand Down
4 changes: 4 additions & 0 deletions clap_builder/src/parser/matches/matched_arg.rs
Expand Up @@ -67,6 +67,10 @@ impl MatchedArg {
self.indices.iter().cloned()
}

pub(crate) fn num_indices(&self) -> usize {
self.indices.len()
}

pub(crate) fn get_index(&self, index: usize) -> Option<usize> {
self.indices.get(index).cloned()
}
Expand Down
6 changes: 6 additions & 0 deletions clap_builder/src/parser/parser.rs
Expand Up @@ -1024,6 +1024,12 @@ impl<'cmd> Parser<'cmd> {
) -> ClapResult<()> {
debug!("Parser::push_arg_values: {raw_vals:?}");

// If there were no values, treat the arg like a flag
if raw_vals.is_empty() {
matcher.add_index_to(arg.get_id(), self.cur_idx.get());
return Ok(());
}

for raw_val in raw_vals {
// update the current index because each value is a distinct index to clap
self.cur_idx.set(self.cur_idx.get() + 1);
Expand Down
17 changes: 17 additions & 0 deletions tests/builder/indices.rs
Expand Up @@ -179,3 +179,20 @@ fn indices_mult_opt_mult_flag() {
assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), [2, 5]);
assert_eq!(m.indices_of("flag").unwrap().collect::<Vec<_>>(), [6]);
}

#[test]
fn indices_mult_optional_value() {
let m = Command::new("myapp")
.args_override_self(true)
.arg(
Arg::new("option")
.short('o')
.num_args(0..=1)
.action(ArgAction::Append),
)
.arg(Arg::new("flag").short('f').action(ArgAction::SetTrue))
.try_get_matches_from(vec!["myapp", "-o", "val1", "-f", "-o", "-f"])
.unwrap();

assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), [2, 4]);
}

0 comments on commit 260fe0e

Please sign in to comment.