Skip to content

Commit 41be1be

Browse files
committed
fix(parser)!: Store args in a group, rather than values
Now that `Id` is public, we can have `ArgMatches` report them. If we have to choose one behavior, this is more universal. The user can still look up the values, this works with groups whose args have different types, and this allows people to make decisions off of it when otherwise there isn't enogh information. Fixes #2317 Fixes #3748
1 parent 004de00 commit 41be1be

File tree

4 files changed

+30
-19
lines changed

4 files changed

+30
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
4040
built-in flags and be copied to all subcommands, instead disable
4141
the built-in flags (`Command::disable_help_flag`,
4242
`Command::disable_version_flag`) and mark the custom flags as `global(true)`.
43+
- Looking up a group in `ArgMatches` now returns the arg `Id`s, rather than the values.
4344
- Various `Arg`, `Command`, and `ArgGroup` calls were switched from accepting `&[]` to `[]` via `IntoIterator`
4445
- *(help)* Make `DeriveDisplayOrder` the default and removed the setting. To sort help, set `next_display_order(None)` (#2808)
4546
- *(help)* Subcommand display order respects `Command::next_display_order` instead of `DeriveDisplayOrder` and using its own initial display order value (#2808)

src/builder/arg_group.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ use crate::util::Id;
4949
/// let err = result.unwrap_err();
5050
/// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
5151
/// ```
52-
/// This next example shows a passing parse of the same scenario
5352
///
53+
/// This next example shows a passing parse of the same scenario
5454
/// ```rust
55-
/// # use clap::{Command, arg, ArgGroup};
55+
/// # use clap::{Command, arg, ArgGroup, Id};
5656
/// let result = Command::new("cmd")
5757
/// .arg(arg!(--"set-ver" <ver> "set the version manually").required(false))
5858
/// .arg(arg!(--major "auto increase major"))
@@ -66,6 +66,13 @@ use crate::util::Id;
6666
/// let matches = result.unwrap();
6767
/// // We may not know which of the args was used, so we can test for the group...
6868
/// assert!(matches.contains_id("vers"));
69+
/// // We can also ask the group which arg was used
70+
/// assert_eq!(matches
71+
/// .get_one::<Id>("vers")
72+
/// .expect("`vers` is required")
73+
/// .as_str(),
74+
/// "major"
75+
/// );
6976
/// // we could also alternatively check each arg individually (not shown here)
7077
/// ```
7178
/// [`ArgGroup::multiple(true)`]: ArgGroup::multiple()

src/parser/parser.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::mkeymap::KeyType;
1717
use crate::output::fmt::Stream;
1818
use crate::output::{fmt::Colorizer, Usage};
1919
use crate::parser::features::suggestions;
20+
use crate::parser::AnyValue;
2021
use crate::parser::{ArgMatcher, SubCommand};
2122
use crate::parser::{Validator, ValueSource};
2223
use crate::util::Id;
@@ -1052,15 +1053,19 @@ impl<'help, 'cmd> Parser<'help, 'cmd> {
10521053
let value_parser = arg.get_value_parser();
10531054
let val = value_parser.parse_ref(self.cmd, Some(arg), &raw_val)?;
10541055

1055-
// Increment or create the group "args"
1056-
for group in self.cmd.groups_for_arg(&arg.id) {
1057-
matcher.add_val_to(&group, val.clone(), raw_val.clone());
1058-
}
1059-
10601056
matcher.add_val_to(&arg.id, val, raw_val);
10611057
matcher.add_index_to(&arg.id, self.cur_idx.get());
10621058
}
10631059

1060+
// Increment or create the group "args"
1061+
for group in self.cmd.groups_for_arg(&arg.id) {
1062+
matcher.add_val_to(
1063+
&group,
1064+
AnyValue::new(arg.get_id().clone()),
1065+
OsString::from(arg.get_id().as_str()),
1066+
);
1067+
}
1068+
10641069
Ok(())
10651070
}
10661071

tests/builder/groups.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::utils;
22

3-
use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command};
3+
use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, Id};
44

55
static REQ_GROUP_USAGE: &str = "error: The following required arguments were not provided:
66
<base|--delete>
@@ -94,10 +94,7 @@ fn group_single_value() {
9494

9595
let m = res.unwrap();
9696
assert!(m.contains_id("grp"));
97-
assert_eq!(
98-
m.get_one::<String>("grp").map(|v| v.as_str()).unwrap(),
99-
"blue"
100-
);
97+
assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
10198
}
10299

103100
#[test]
@@ -141,13 +138,7 @@ fn group_multi_value_single_arg() {
141138

142139
let m = res.unwrap();
143140
assert!(m.contains_id("grp"));
144-
assert_eq!(
145-
&*m.get_many::<String>("grp")
146-
.unwrap()
147-
.map(|v| v.as_str())
148-
.collect::<Vec<_>>(),
149-
&["blue", "red", "green"]
150-
);
141+
assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
151142
}
152143

153144
#[test]
@@ -234,6 +225,13 @@ fn required_group_multiple_args() {
234225
let m = result.unwrap();
235226
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
236227
assert!(*m.get_one::<bool>("color").expect("defaulted by clap"));
228+
assert_eq!(
229+
&*m.get_many::<Id>("req")
230+
.unwrap()
231+
.map(|v| v.as_str())
232+
.collect::<Vec<_>>(),
233+
&["flag", "color"]
234+
);
237235
}
238236

239237
#[test]

0 commit comments

Comments
 (0)