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