• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Internal
2 use crate::builder::IntoResettable;
3 use crate::util::Id;
4 
5 /// Family of related [arguments].
6 ///
7 /// By placing arguments in a logical group, you can create easier requirement and
8 /// exclusion rules instead of having to list each argument individually, or when you want a rule
9 /// to apply "any but not all" arguments.
10 ///
11 /// For instance, you can make an entire `ArgGroup` required. If [`ArgGroup::multiple(true)`] is
12 /// set, this means that at least one argument from that group must be present. If
13 /// [`ArgGroup::multiple(false)`] is set (the default), one and *only* one must be present.
14 ///
15 /// You can also do things such as name an entire `ArgGroup` as a [conflict] or [requirement] for
16 /// another argument, meaning any of the arguments that belong to that group will cause a failure
17 /// if present, or must be present respectively.
18 ///
19 /// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
20 /// present out of a given set. Imagine that you had multiple arguments, and you want one of them
21 /// to be required, but making all of them required isn't feasible because perhaps they conflict
22 /// with each other. For example, lets say that you were building an application where one could
23 /// set a given version number by supplying a string with an option argument, i.e.
24 /// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number
25 /// and simply incrementing one of the three numbers. So you create three flags `--major`,
26 /// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to
27 /// specify that *at least one* of them is used. For this, you can create a group.
28 ///
29 /// Finally, you may use `ArgGroup`s to pull a value from a group of arguments when you don't care
30 /// exactly which argument was actually used at runtime.
31 ///
32 /// # Examples
33 ///
34 /// The following example demonstrates using an `ArgGroup` to ensure that one, and only one, of
35 /// the arguments from the specified group is present at runtime.
36 ///
37 /// ```rust
38 /// # use clap::{Command, arg, ArgGroup, error::ErrorKind};
39 /// let result = Command::new("cmd")
40 ///     .arg(arg!(--"set-ver" <ver> "set the version manually"))
41 ///     .arg(arg!(--major           "auto increase major"))
42 ///     .arg(arg!(--minor           "auto increase minor"))
43 ///     .arg(arg!(--patch           "auto increase patch"))
44 ///     .group(ArgGroup::new("vers")
45 ///          .args(["set-ver", "major", "minor", "patch"])
46 ///          .required(true))
47 ///     .try_get_matches_from(vec!["cmd", "--major", "--patch"]);
48 /// // Because we used two args in the group it's an error
49 /// assert!(result.is_err());
50 /// let err = result.unwrap_err();
51 /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
52 /// ```
53 ///
54 /// This next example shows a passing parse of the same scenario
55 /// ```rust
56 /// # use clap::{Command, arg, ArgGroup, Id};
57 /// let result = Command::new("cmd")
58 ///     .arg(arg!(--"set-ver" <ver> "set the version manually"))
59 ///     .arg(arg!(--major           "auto increase major"))
60 ///     .arg(arg!(--minor           "auto increase minor"))
61 ///     .arg(arg!(--patch           "auto increase patch"))
62 ///     .group(ArgGroup::new("vers")
63 ///          .args(["set-ver", "major", "minor","patch"])
64 ///          .required(true))
65 ///     .try_get_matches_from(vec!["cmd", "--major"]);
66 /// assert!(result.is_ok());
67 /// let matches = result.unwrap();
68 /// // We may not know which of the args was used, so we can test for the group...
69 /// assert!(matches.contains_id("vers"));
70 /// // We can also ask the group which arg was used
71 /// assert_eq!(matches
72 ///     .get_one::<Id>("vers")
73 ///     .expect("`vers` is required")
74 ///     .as_str(),
75 ///     "major"
76 /// );
77 /// // we could also alternatively check each arg individually (not shown here)
78 /// ```
79 /// [`ArgGroup::multiple(true)`]: ArgGroup::multiple()
80 ///
81 /// [`ArgGroup::multiple(false)`]: ArgGroup::multiple()
82 /// [arguments]: crate::Arg
83 /// [conflict]: crate::Arg::conflicts_with()
84 /// [requirement]: crate::Arg::requires()
85 #[derive(Default, Clone, Debug, PartialEq, Eq)]
86 pub struct ArgGroup {
87     pub(crate) id: Id,
88     pub(crate) args: Vec<Id>,
89     pub(crate) required: bool,
90     pub(crate) requires: Vec<Id>,
91     pub(crate) conflicts: Vec<Id>,
92     pub(crate) multiple: bool,
93 }
94 
95 /// # Builder
96 impl ArgGroup {
97     /// Create a `ArgGroup` using a unique name.
98     ///
99     /// The name will be used to get values from the group or refer to the group inside of conflict
100     /// and requirement rules.
101     ///
102     /// # Examples
103     ///
104     /// ```rust
105     /// # use clap::{Command, ArgGroup};
106     /// ArgGroup::new("config")
107     /// # ;
108     /// ```
new(id: impl Into<Id>) -> Self109     pub fn new(id: impl Into<Id>) -> Self {
110         ArgGroup::default().id(id)
111     }
112 
113     /// Sets the group name.
114     ///
115     /// # Examples
116     ///
117     /// ```rust
118     /// # use clap::{Command, ArgGroup};
119     /// ArgGroup::default().id("config")
120     /// # ;
121     /// ```
122     #[must_use]
id(mut self, id: impl Into<Id>) -> Self123     pub fn id(mut self, id: impl Into<Id>) -> Self {
124         self.id = id.into();
125         self
126     }
127 
128     /// Adds an [argument] to this group by name
129     ///
130     /// # Examples
131     ///
132     /// ```rust
133     /// # use clap::{Command, Arg, ArgGroup, ArgAction};
134     /// let m = Command::new("myprog")
135     ///     .arg(Arg::new("flag")
136     ///         .short('f')
137     ///         .action(ArgAction::SetTrue))
138     ///     .arg(Arg::new("color")
139     ///         .short('c')
140     ///         .action(ArgAction::SetTrue))
141     ///     .group(ArgGroup::new("req_flags")
142     ///         .arg("flag")
143     ///         .arg("color"))
144     ///     .get_matches_from(vec!["myprog", "-f"]);
145     /// // maybe we don't know which of the two flags was used...
146     /// assert!(m.contains_id("req_flags"));
147     /// // but we can also check individually if needed
148     /// assert!(m.contains_id("flag"));
149     /// ```
150     /// [argument]: crate::Arg
151     #[must_use]
arg(mut self, arg_id: impl IntoResettable<Id>) -> Self152     pub fn arg(mut self, arg_id: impl IntoResettable<Id>) -> Self {
153         if let Some(arg_id) = arg_id.into_resettable().into_option() {
154             self.args.push(arg_id);
155         } else {
156             self.args.clear();
157         }
158         self
159     }
160 
161     /// Adds multiple [arguments] to this group by name
162     ///
163     /// # Examples
164     ///
165     /// ```rust
166     /// # use clap::{Command, Arg, ArgGroup, ArgAction};
167     /// let m = Command::new("myprog")
168     ///     .arg(Arg::new("flag")
169     ///         .short('f')
170     ///         .action(ArgAction::SetTrue))
171     ///     .arg(Arg::new("color")
172     ///         .short('c')
173     ///         .action(ArgAction::SetTrue))
174     ///     .group(ArgGroup::new("req_flags")
175     ///         .args(["flag", "color"]))
176     ///     .get_matches_from(vec!["myprog", "-f"]);
177     /// // maybe we don't know which of the two flags was used...
178     /// assert!(m.contains_id("req_flags"));
179     /// // but we can also check individually if needed
180     /// assert!(m.contains_id("flag"));
181     /// ```
182     /// [arguments]: crate::Arg
183     #[must_use]
args(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self184     pub fn args(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self {
185         for n in ns {
186             self = self.arg(n);
187         }
188         self
189     }
190 
191     /// Getters for all args. It will return a vector of `Id`
192     ///
193     /// # Example
194     ///
195     /// ```rust
196     /// # use clap::{ArgGroup};
197     /// let args: Vec<&str> = vec!["a1".into(), "a4".into()];
198     /// let grp = ArgGroup::new("program").args(&args);
199     ///
200     /// for (pos, arg) in grp.get_args().enumerate() {
201     ///     assert_eq!(*arg, args[pos]);
202     /// }
203     /// ```
get_args(&self) -> impl Iterator<Item = &Id>204     pub fn get_args(&self) -> impl Iterator<Item = &Id> {
205         self.args.iter()
206     }
207 
208     /// Allows more than one of the [`Arg`]s in this group to be used. (Default: `false`)
209     ///
210     /// # Examples
211     ///
212     /// Notice in this example we use *both* the `-f` and `-c` flags which are both part of the
213     /// group
214     ///
215     /// ```rust
216     /// # use clap::{Command, Arg, ArgGroup, ArgAction};
217     /// let m = Command::new("myprog")
218     ///     .arg(Arg::new("flag")
219     ///         .short('f')
220     ///         .action(ArgAction::SetTrue))
221     ///     .arg(Arg::new("color")
222     ///         .short('c')
223     ///         .action(ArgAction::SetTrue))
224     ///     .group(ArgGroup::new("req_flags")
225     ///         .args(["flag", "color"])
226     ///         .multiple(true))
227     ///     .get_matches_from(vec!["myprog", "-f", "-c"]);
228     /// // maybe we don't know which of the two flags was used...
229     /// assert!(m.contains_id("req_flags"));
230     /// ```
231     /// In this next example, we show the default behavior (i.e. `multiple(false)) which will throw
232     /// an error if more than one of the args in the group was used.
233     ///
234     /// ```rust
235     /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
236     /// let result = Command::new("myprog")
237     ///     .arg(Arg::new("flag")
238     ///         .short('f')
239     ///         .action(ArgAction::SetTrue))
240     ///     .arg(Arg::new("color")
241     ///         .short('c')
242     ///         .action(ArgAction::SetTrue))
243     ///     .group(ArgGroup::new("req_flags")
244     ///         .args(["flag", "color"]))
245     ///     .try_get_matches_from(vec!["myprog", "-f", "-c"]);
246     /// // Because we used both args in the group it's an error
247     /// assert!(result.is_err());
248     /// let err = result.unwrap_err();
249     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
250     /// ```
251     ///
252     /// [`Arg`]: crate::Arg
253     #[inline]
254     #[must_use]
multiple(mut self, yes: bool) -> Self255     pub fn multiple(mut self, yes: bool) -> Self {
256         self.multiple = yes;
257         self
258     }
259 
260     /// Return true if the group allows more than one of the arguments
261     /// in this group to be used. (Default: `false`)
262     ///
263     /// # Example
264     ///
265     /// ```rust
266     /// # use clap::{ArgGroup};
267     /// let mut group = ArgGroup::new("myprog")
268     ///     .args(["f", "c"])
269     ///     .multiple(true);
270     ///
271     /// assert!(group.is_multiple());
272     /// ```
is_multiple(&mut self) -> bool273     pub fn is_multiple(&mut self) -> bool {
274         self.multiple
275     }
276 
277     /// Require an argument from the group to be present when parsing.
278     ///
279     /// This is unless conflicting with another argument.  A required group will be displayed in
280     /// the usage string of the application in the format `<arg|arg2|arg3>`.
281     ///
282     /// **NOTE:** This setting only applies to the current [`Command`] / [`Subcommand`]s, and not
283     /// globally.
284     ///
285     /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with
286     /// `ArgGroup::required(true)` states, "One and *only one* arg must be used from this group.
287     /// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which
288     /// states, '*At least* one arg from this group must be used. Using multiple is OK."
289     ///
290     /// # Examples
291     ///
292     /// ```rust
293     /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
294     /// let result = Command::new("myprog")
295     ///     .arg(Arg::new("flag")
296     ///         .short('f')
297     ///         .action(ArgAction::SetTrue))
298     ///     .arg(Arg::new("color")
299     ///         .short('c')
300     ///         .action(ArgAction::SetTrue))
301     ///     .group(ArgGroup::new("req_flags")
302     ///         .args(["flag", "color"])
303     ///         .required(true))
304     ///     .try_get_matches_from(vec!["myprog"]);
305     /// // Because we didn't use any of the args in the group, it's an error
306     /// assert!(result.is_err());
307     /// let err = result.unwrap_err();
308     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
309     /// ```
310     ///
311     /// [`Subcommand`]: crate::Subcommand
312     /// [`ArgGroup::multiple`]: ArgGroup::multiple()
313     /// [`Command`]: crate::Command
314     #[inline]
315     #[must_use]
required(mut self, yes: bool) -> Self316     pub fn required(mut self, yes: bool) -> Self {
317         self.required = yes;
318         self
319     }
320 
321     /// Specify an argument or group that must be present when this group is.
322     ///
323     /// This is not to be confused with a [required group]. Requirement rules function just like
324     /// [argument requirement rules], you can name other arguments or groups that must be present
325     /// when any one of the arguments from this group is used.
326     ///
327     /// **NOTE:** The name provided may be an argument or group name
328     ///
329     /// # Examples
330     ///
331     /// ```rust
332     /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
333     /// let result = Command::new("myprog")
334     ///     .arg(Arg::new("flag")
335     ///         .short('f')
336     ///         .action(ArgAction::SetTrue))
337     ///     .arg(Arg::new("color")
338     ///         .short('c')
339     ///         .action(ArgAction::SetTrue))
340     ///     .arg(Arg::new("debug")
341     ///         .short('d')
342     ///         .action(ArgAction::SetTrue))
343     ///     .group(ArgGroup::new("req_flags")
344     ///         .args(["flag", "color"])
345     ///         .requires("debug"))
346     ///     .try_get_matches_from(vec!["myprog", "-c"]);
347     /// // because we used an arg from the group, and the group requires "-d" to be used, it's an
348     /// // error
349     /// assert!(result.is_err());
350     /// let err = result.unwrap_err();
351     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
352     /// ```
353     /// [required group]: ArgGroup::required()
354     /// [argument requirement rules]: crate::Arg::requires()
355     #[must_use]
requires(mut self, id: impl IntoResettable<Id>) -> Self356     pub fn requires(mut self, id: impl IntoResettable<Id>) -> Self {
357         if let Some(id) = id.into_resettable().into_option() {
358             self.requires.push(id);
359         } else {
360             self.requires.clear();
361         }
362         self
363     }
364 
365     /// Specify arguments or groups that must be present when this group is.
366     ///
367     /// This is not to be confused with a [required group]. Requirement rules function just like
368     /// [argument requirement rules], you can name other arguments or groups that must be present
369     /// when one of the arguments from this group is used.
370     ///
371     /// **NOTE:** The names provided may be an argument or group name
372     ///
373     /// # Examples
374     ///
375     /// ```rust
376     /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
377     /// let result = Command::new("myprog")
378     ///     .arg(Arg::new("flag")
379     ///         .short('f')
380     ///         .action(ArgAction::SetTrue))
381     ///     .arg(Arg::new("color")
382     ///         .short('c')
383     ///         .action(ArgAction::SetTrue))
384     ///     .arg(Arg::new("debug")
385     ///         .short('d')
386     ///         .action(ArgAction::SetTrue))
387     ///     .arg(Arg::new("verb")
388     ///         .short('v')
389     ///         .action(ArgAction::SetTrue))
390     ///     .group(ArgGroup::new("req_flags")
391     ///         .args(["flag", "color"])
392     ///         .requires_all(["debug", "verb"]))
393     ///     .try_get_matches_from(vec!["myprog", "-c", "-d"]);
394     /// // because we used an arg from the group, and the group requires "-d" and "-v" to be used,
395     /// // yet we only used "-d" it's an error
396     /// assert!(result.is_err());
397     /// let err = result.unwrap_err();
398     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
399     /// ```
400     /// [required group]: ArgGroup::required()
401     /// [argument requirement rules]: crate::Arg::requires_ifs()
402     #[must_use]
requires_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self403     pub fn requires_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self {
404         for n in ns {
405             self = self.requires(n);
406         }
407         self
408     }
409 
410     /// Specify an argument or group that must **not** be present when this group is.
411     ///
412     /// Exclusion (aka conflict) rules function just like [argument exclusion rules], you can name
413     /// other arguments or groups that must *not* be present when one of the arguments from this
414     /// group are used.
415     ///
416     /// **NOTE:** The name provided may be an argument, or group name
417     ///
418     /// # Examples
419     ///
420     /// ```rust
421     /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
422     /// let result = Command::new("myprog")
423     ///     .arg(Arg::new("flag")
424     ///         .short('f')
425     ///         .action(ArgAction::SetTrue))
426     ///     .arg(Arg::new("color")
427     ///         .short('c')
428     ///         .action(ArgAction::SetTrue))
429     ///     .arg(Arg::new("debug")
430     ///         .short('d')
431     ///         .action(ArgAction::SetTrue))
432     ///     .group(ArgGroup::new("req_flags")
433     ///         .args(["flag", "color"])
434     ///         .conflicts_with("debug"))
435     ///     .try_get_matches_from(vec!["myprog", "-c", "-d"]);
436     /// // because we used an arg from the group, and the group conflicts with "-d", it's an error
437     /// assert!(result.is_err());
438     /// let err = result.unwrap_err();
439     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
440     /// ```
441     /// [argument exclusion rules]: crate::Arg::conflicts_with()
442     #[must_use]
conflicts_with(mut self, id: impl IntoResettable<Id>) -> Self443     pub fn conflicts_with(mut self, id: impl IntoResettable<Id>) -> Self {
444         if let Some(id) = id.into_resettable().into_option() {
445             self.conflicts.push(id);
446         } else {
447             self.conflicts.clear();
448         }
449         self
450     }
451 
452     /// Specify arguments or groups that must **not** be present when this group is.
453     ///
454     /// Exclusion rules function just like [argument exclusion rules], you can name other arguments
455     /// or groups that must *not* be present when one of the arguments from this group are used.
456     ///
457     /// **NOTE:** The names provided may be an argument, or group name
458     ///
459     /// # Examples
460     ///
461     /// ```rust
462     /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
463     /// let result = Command::new("myprog")
464     ///     .arg(Arg::new("flag")
465     ///         .short('f')
466     ///         .action(ArgAction::SetTrue))
467     ///     .arg(Arg::new("color")
468     ///         .short('c')
469     ///         .action(ArgAction::SetTrue))
470     ///     .arg(Arg::new("debug")
471     ///         .short('d')
472     ///         .action(ArgAction::SetTrue))
473     ///     .arg(Arg::new("verb")
474     ///         .short('v')
475     ///         .action(ArgAction::SetTrue))
476     ///     .group(ArgGroup::new("req_flags")
477     ///         .args(["flag", "color"])
478     ///         .conflicts_with_all(["debug", "verb"]))
479     ///     .try_get_matches_from(vec!["myprog", "-c", "-v"]);
480     /// // because we used an arg from the group, and the group conflicts with either "-v" or "-d"
481     /// // it's an error
482     /// assert!(result.is_err());
483     /// let err = result.unwrap_err();
484     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
485     /// ```
486     ///
487     /// [argument exclusion rules]: crate::Arg::conflicts_with_all()
488     #[must_use]
conflicts_with_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self489     pub fn conflicts_with_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self {
490         for n in ns {
491             self = self.conflicts_with(n);
492         }
493         self
494     }
495 }
496 
497 /// # Reflection
498 impl ArgGroup {
499     /// Get the name of the group
500     #[inline]
get_id(&self) -> &Id501     pub fn get_id(&self) -> &Id {
502         &self.id
503     }
504 
505     /// Reports whether [`ArgGroup::required`] is set
506     #[inline]
is_required_set(&self) -> bool507     pub fn is_required_set(&self) -> bool {
508         self.required
509     }
510 }
511 
512 impl From<&'_ ArgGroup> for ArgGroup {
from(g: &ArgGroup) -> Self513     fn from(g: &ArgGroup) -> Self {
514         g.clone()
515     }
516 }
517 
518 #[cfg(test)]
519 mod test {
520     use super::*;
521 
522     #[test]
groups()523     fn groups() {
524         let g = ArgGroup::new("test")
525             .arg("a1")
526             .arg("a4")
527             .args(["a2", "a3"])
528             .required(true)
529             .conflicts_with("c1")
530             .conflicts_with_all(["c2", "c3"])
531             .conflicts_with("c4")
532             .requires("r1")
533             .requires_all(["r2", "r3"])
534             .requires("r4");
535 
536         let args: Vec<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
537         let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
538         let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
539 
540         assert_eq!(g.args, args);
541         assert_eq!(g.requires, reqs);
542         assert_eq!(g.conflicts, confs);
543     }
544 
545     #[test]
test_from()546     fn test_from() {
547         let g = ArgGroup::new("test")
548             .arg("a1")
549             .arg("a4")
550             .args(["a2", "a3"])
551             .required(true)
552             .conflicts_with("c1")
553             .conflicts_with_all(["c2", "c3"])
554             .conflicts_with("c4")
555             .requires("r1")
556             .requires_all(["r2", "r3"])
557             .requires("r4");
558 
559         let args: Vec<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
560         let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
561         let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
562 
563         let g2 = ArgGroup::from(&g);
564         assert_eq!(g2.args, args);
565         assert_eq!(g2.requires, reqs);
566         assert_eq!(g2.conflicts, confs);
567     }
568 
569     // This test will *fail to compile* if ArgGroup is not Send + Sync
570     #[test]
arg_group_send_sync()571     fn arg_group_send_sync() {
572         fn foo<T: Send + Sync>(_: T) {}
573         foo(ArgGroup::new("test"))
574     }
575 
576     #[test]
arg_group_expose_is_multiple_helper()577     fn arg_group_expose_is_multiple_helper() {
578         let args: Vec<Id> = vec!["a1".into(), "a4".into()];
579 
580         let mut grp_multiple = ArgGroup::new("test_multiple").args(&args).multiple(true);
581         assert!(grp_multiple.is_multiple());
582 
583         let mut grp_not_multiple = ArgGroup::new("test_multiple").args(&args).multiple(false);
584         assert!(!grp_not_multiple.is_multiple());
585     }
586 
587     #[test]
arg_group_expose_get_args_helper()588     fn arg_group_expose_get_args_helper() {
589         let args: Vec<Id> = vec!["a1".into(), "a4".into()];
590         let grp = ArgGroup::new("program").args(&args);
591 
592         for (pos, arg) in grp.get_args().enumerate() {
593             assert_eq!(*arg, args[pos]);
594         }
595     }
596 }
597