group
A set of flags that relate to one another as a set.
flag "--file <file>"
flag "--url <url>"
flag "--stdin"
// at most one of these
group "input" "--file" "--url" "--stdin"
// exactly one of these: one is required, and only one is allowed
group "input" "--file" "--url" "--stdin" required=#true
// at least one of these
group "input" "--file" "--url" "--stdin" required=#true multiple=#true
// the same, written out when the members do not sit comfortably on one line
group "input" required=#true {
flag "--file" "--url"
flag "--stdin"
}The name comes first, then the members, spelled the way every other relationship names a flag — --long or -s. A group lives on the command whose flags it names, and a group naming global flags belongs to the command that declares them.
What the two properties mean
required | multiple | meaning |
|---|---|---|
| — | — | at most one of these |
#true | — | exactly one of these |
| — | #true | nothing is enforced |
#true | #true | at least one of these |
Both default to false, so a bare group is mutual exclusion. These are clap's two properties read the same way, so a spec generated from a clap command means by them what clap meant.
Why not just conflicts
For "at most one", conflicts says the same thing — but it says it once per pair, so three flags need three declarations and six need fifteen, and a flag added later has to be added to every sibling.
The part conflicts cannot say at all is required. "One of these is needed" is a statement about the set: no rule written on an individual flag expresses it, because no individual flag is the one that must be given.
What counts as given
The two halves of a group are two kinds of rule, and they read a default differently.
Exclusivity counts what was supplied — from the command line or from a member's env variable — and not what was defaulted. This is the rule conflicts follows, and it has to be: a defaulted member counted as supplied would collide with the sibling the user actually typed, and refuse a correct command line.
Requiredness asks whether a member ended up with a value, and a default is a value. This is the rule requires and plain required follow. A required group whose member has a default is therefore always satisfied — which is worth noticing when you write one, since it means the group enforces nothing.
Members
A group needs at least two members, and naming fewer is an error where it is written rather than a rule that quietly enforces nothing. A group of one is a statement about that flag, which belongs on the flag as required or requires.
Only flags can be members. clap allows a positional in a group; a spec generated from such a command keeps the flags and drops the positional, since the spec has no way to name one in a relationship and a selector matching nothing would read as a rule that holds.
Members are counted by the flag they name, not by the selector, so a group listing both -f and --file holds one member and not two. Listing both is redundant rather than wrong, and a flag is never in conflict with itself.
From the derive
#[derive(usage::Cli)] writes the same group with membership on the fields and the properties on the struct:
#[usage(group("input", required))]
struct Ex {
#[usage(long, group = "input")]
file: Option<String>,
#[usage(long, group = "input")]
url: Option<String>,
}The #[usage(group(...))] line can be left out when the group is a plain "at most one". A group with fewer than two members, or a declaration no field joins, is a compile error rather than a rule that quietly holds for nothing.
Coming from clap
ArgGroup carries across, with required and multiple read the same way. A group that is multiple without being required states no rule at all, so it is dropped rather than written out — which matters more than it sounds, because clap's derive creates exactly such a group for every #[derive(Args)] struct, named after the struct, to make flatten work. Those are clap's bookkeeping, not a rule anyone declared.