• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Internal
2 use crate::util::{Id, Key};
3 
4 #[cfg(feature = "yaml")]
5 use yaml_rust::Yaml;
6 
7 /// Family of related [arguments].
8 ///
9 /// By placing arguments in a logical group, you can create easier requirement and
10 /// exclusion rules instead of having to list each argument individually, or when you want a rule
11 /// to apply "any but not all" arguments.
12 ///
13 /// For instance, you can make an entire `ArgGroup` required. If [`ArgGroup::multiple(true)`] is
14 /// set, this means that at least one argument from that group must be present. If
15 /// [`ArgGroup::multiple(false)`] is set (the default), one and *only* one must be present.
16 ///
17 /// You can also do things such as name an entire `ArgGroup` as a [conflict] or [requirement] for
18 /// another argument, meaning any of the arguments that belong to that group will cause a failure
19 /// if present, or must be present respectively.
20 ///
21 /// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
22 /// present out of a given set. Imagine that you had multiple arguments, and you want one of them
23 /// to be required, but making all of them required isn't feasible because perhaps they conflict
24 /// with each other. For example, lets say that you were building an application where one could
25 /// set a given version number by supplying a string with an option argument, i.e.
26 /// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number
27 /// and simply incrementing one of the three numbers. So you create three flags `--major`,
28 /// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to
29 /// specify that *at least one* of them is used. For this, you can create a group.
30 ///
31 /// Finally, you may use `ArgGroup`s to pull a value from a group of arguments when you don't care
32 /// exactly which argument was actually used at runtime.
33 ///
34 /// # Examples
35 ///
36 /// The following example demonstrates using an `ArgGroup` to ensure that one, and only one, of
37 /// the arguments from the specified group is present at runtime.
38 ///
39 /// ```rust
40 /// # use clap::{Command, arg, ArgGroup, ErrorKind};
41 /// let result = Command::new("cmd")
42 ///     .arg(arg!(--"set-ver" <ver> "set the version manually").required(false))
43 ///     .arg(arg!(--major           "auto increase major"))
44 ///     .arg(arg!(--minor           "auto increase minor"))
45 ///     .arg(arg!(--patch           "auto increase patch"))
46 ///     .group(ArgGroup::new("vers")
47 ///          .args(&["set-ver", "major", "minor", "patch"])
48 ///          .required(true))
49 ///     .try_get_matches_from(vec!["cmd", "--major", "--patch"]);
50 /// // Because we used two args in the group it's an error
51 /// assert!(result.is_err());
52 /// let err = result.unwrap_err();
53 /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
54 /// ```
55 /// This next example shows a passing parse of the same scenario
56 ///
57 /// ```rust
58 /// # use clap::{Command, arg, ArgGroup};
59 /// let result = Command::new("cmd")
60 ///     .arg(arg!(--"set-ver" <ver> "set the version manually").required(false))
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 could also alternatively check each arg individually (not shown here)
73 /// ```
74 /// [`ArgGroup::multiple(true)`]: ArgGroup::multiple()
75 ///
76 /// [`ArgGroup::multiple(false)`]: ArgGroup::multiple()
77 /// [arguments]: crate::Arg
78 /// [conflict]: crate::Arg::conflicts_with()
79 /// [requirement]: crate::Arg::requires()
80 #[derive(Default, Debug, PartialEq, Eq)]
81 pub struct ArgGroup<'help> {
82     pub(crate) id: Id,
83     pub(crate) name: &'help str,
84     pub(crate) args: Vec<Id>,
85     pub(crate) required: bool,
86     pub(crate) requires: Vec<Id>,
87     pub(crate) conflicts: Vec<Id>,
88     pub(crate) multiple: bool,
89 }
90 
91 impl<'help> ArgGroup<'help> {
with_id(id: Id) -> Self92     pub(crate) fn with_id(id: Id) -> Self {
93         ArgGroup {
94             id,
95             ..ArgGroup::default()
96         }
97     }
98 
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::{Command, ArgGroup};
108     /// ArgGroup::new("config")
109     /// # ;
110     /// ```
new<S: Into<&'help str>>(n: S) -> Self111     pub fn new<S: Into<&'help str>>(n: S) -> Self {
112         ArgGroup::default().id(n)
113     }
114 
115     /// Sets the group name.
116     ///
117     /// # Examples
118     ///
119     /// ```rust
120     /// # use clap::{Command, ArgGroup};
121     /// ArgGroup::default().name("config")
122     /// # ;
123     /// ```
124     #[must_use]
id<S: Into<&'help str>>(mut self, n: S) -> Self125     pub fn id<S: Into<&'help str>>(mut self, n: S) -> Self {
126         self.name = n.into();
127         self.id = Id::from(self.name);
128         self
129     }
130 
131     /// Deprecated, replaced with [`ArgGroup::id`]
132     ///
133     /// Builder: replaced `group.name(...)` with `group.id(...)`
134     #[cfg_attr(
135         feature = "deprecated",
136         deprecated(
137             since = "3.1.0",
138             note = "Replaced with `ArgGroup::id`
139 
140 Builder: replaced `group.name(...)` with `group.id(...)`
141 "
142         )
143     )]
name<S: Into<&'help str>>(self, n: S) -> Self144     pub fn name<S: Into<&'help str>>(self, n: S) -> Self {
145         self.id(n)
146     }
147 
148     /// Adds an [argument] to this group by name
149     ///
150     /// # Examples
151     ///
152     /// ```rust
153     /// # use clap::{Command, Arg, ArgGroup};
154     /// let m = Command::new("myprog")
155     ///     .arg(Arg::new("flag")
156     ///         .short('f'))
157     ///     .arg(Arg::new("color")
158     ///         .short('c'))
159     ///     .group(ArgGroup::new("req_flags")
160     ///         .arg("flag")
161     ///         .arg("color"))
162     ///     .get_matches_from(vec!["myprog", "-f"]);
163     /// // maybe we don't know which of the two flags was used...
164     /// assert!(m.contains_id("req_flags"));
165     /// // but we can also check individually if needed
166     /// assert!(m.contains_id("flag"));
167     /// ```
168     /// [argument]: crate::Arg
169     #[must_use]
arg<T: Key>(mut self, arg_id: T) -> Self170     pub fn arg<T: Key>(mut self, arg_id: T) -> Self {
171         self.args.push(arg_id.into());
172         self
173     }
174 
175     /// Adds multiple [arguments] to this group by name
176     ///
177     /// # Examples
178     ///
179     /// ```rust
180     /// # use clap::{Command, Arg, ArgGroup};
181     /// let m = Command::new("myprog")
182     ///     .arg(Arg::new("flag")
183     ///         .short('f'))
184     ///     .arg(Arg::new("color")
185     ///         .short('c'))
186     ///     .group(ArgGroup::new("req_flags")
187     ///         .args(&["flag", "color"]))
188     ///     .get_matches_from(vec!["myprog", "-f"]);
189     /// // maybe we don't know which of the two flags was used...
190     /// assert!(m.contains_id("req_flags"));
191     /// // but we can also check individually if needed
192     /// assert!(m.contains_id("flag"));
193     /// ```
194     /// [arguments]: crate::Arg
195     #[must_use]
args<T: Key>(mut self, ns: &[T]) -> Self196     pub fn args<T: Key>(mut self, ns: &[T]) -> Self {
197         for n in ns {
198             self = self.arg(n);
199         }
200         self
201     }
202 
203     /// Allows more than one of the [`Arg`]s in this group to be used. (Default: `false`)
204     ///
205     /// # Examples
206     ///
207     /// Notice in this example we use *both* the `-f` and `-c` flags which are both part of the
208     /// group
209     ///
210     /// ```rust
211     /// # use clap::{Command, Arg, ArgGroup};
212     /// let m = Command::new("myprog")
213     ///     .arg(Arg::new("flag")
214     ///         .short('f'))
215     ///     .arg(Arg::new("color")
216     ///         .short('c'))
217     ///     .group(ArgGroup::new("req_flags")
218     ///         .args(&["flag", "color"])
219     ///         .multiple(true))
220     ///     .get_matches_from(vec!["myprog", "-f", "-c"]);
221     /// // maybe we don't know which of the two flags was used...
222     /// assert!(m.contains_id("req_flags"));
223     /// ```
224     /// In this next example, we show the default behavior (i.e. `multiple(false)) which will throw
225     /// an error if more than one of the args in the group was used.
226     ///
227     /// ```rust
228     /// # use clap::{Command, Arg, ArgGroup, ErrorKind};
229     /// let result = Command::new("myprog")
230     ///     .arg(Arg::new("flag")
231     ///         .short('f'))
232     ///     .arg(Arg::new("color")
233     ///         .short('c'))
234     ///     .group(ArgGroup::new("req_flags")
235     ///         .args(&["flag", "color"]))
236     ///     .try_get_matches_from(vec!["myprog", "-f", "-c"]);
237     /// // Because we used both args in the group it's an error
238     /// assert!(result.is_err());
239     /// let err = result.unwrap_err();
240     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
241     /// ```
242     ///
243     /// [`Arg`]: crate::Arg
244     #[inline]
245     #[must_use]
multiple(mut self, yes: bool) -> Self246     pub fn multiple(mut self, yes: bool) -> Self {
247         self.multiple = yes;
248         self
249     }
250 
251     /// Require an argument from the group to be present when parsing.
252     ///
253     /// This is unless conflicting with another argument.  A required group will be displayed in
254     /// the usage string of the application in the format `<arg|arg2|arg3>`.
255     ///
256     /// **NOTE:** This setting only applies to the current [`Command`] / [`Subcommand`]s, and not
257     /// globally.
258     ///
259     /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with
260     /// `ArgGroup::required(true)` states, "One and *only one* arg must be used from this group.
261     /// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which
262     /// states, '*At least* one arg from this group must be used. Using multiple is OK."
263     ///
264     /// # Examples
265     ///
266     /// ```rust
267     /// # use clap::{Command, Arg, ArgGroup, ErrorKind};
268     /// let result = Command::new("myprog")
269     ///     .arg(Arg::new("flag")
270     ///         .short('f'))
271     ///     .arg(Arg::new("color")
272     ///         .short('c'))
273     ///     .group(ArgGroup::new("req_flags")
274     ///         .args(&["flag", "color"])
275     ///         .required(true))
276     ///     .try_get_matches_from(vec!["myprog"]);
277     /// // Because we didn't use any of the args in the group, it's an error
278     /// assert!(result.is_err());
279     /// let err = result.unwrap_err();
280     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
281     /// ```
282     ///
283     /// [`Subcommand`]: crate::Subcommand
284     /// [`ArgGroup::multiple`]: ArgGroup::multiple()
285     /// [`Command`]: crate::Command
286     #[inline]
287     #[must_use]
required(mut self, yes: bool) -> Self288     pub fn required(mut self, yes: bool) -> Self {
289         self.required = yes;
290         self
291     }
292 
293     /// Specify an argument or group that must be present when this group is.
294     ///
295     /// This is not to be confused with a [required group]. Requirement rules function just like
296     /// [argument requirement rules], you can name other arguments or groups that must be present
297     /// when any one of the arguments from this group is used.
298     ///
299     /// **NOTE:** The name provided may be an argument or group name
300     ///
301     /// # Examples
302     ///
303     /// ```rust
304     /// # use clap::{Command, Arg, ArgGroup, ErrorKind};
305     /// let result = Command::new("myprog")
306     ///     .arg(Arg::new("flag")
307     ///         .short('f'))
308     ///     .arg(Arg::new("color")
309     ///         .short('c'))
310     ///     .arg(Arg::new("debug")
311     ///         .short('d'))
312     ///     .group(ArgGroup::new("req_flags")
313     ///         .args(&["flag", "color"])
314     ///         .requires("debug"))
315     ///     .try_get_matches_from(vec!["myprog", "-c"]);
316     /// // because we used an arg from the group, and the group requires "-d" to be used, it's an
317     /// // error
318     /// assert!(result.is_err());
319     /// let err = result.unwrap_err();
320     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
321     /// ```
322     /// [required group]: ArgGroup::required()
323     /// [argument requirement rules]: crate::Arg::requires()
324     #[must_use]
requires<T: Key>(mut self, id: T) -> Self325     pub fn requires<T: Key>(mut self, id: T) -> Self {
326         self.requires.push(id.into());
327         self
328     }
329 
330     /// Specify arguments or groups that must be present when this group is.
331     ///
332     /// This is not to be confused with a [required group]. Requirement rules function just like
333     /// [argument requirement rules], you can name other arguments or groups that must be present
334     /// when one of the arguments from this group is used.
335     ///
336     /// **NOTE:** The names provided may be an argument or group name
337     ///
338     /// # Examples
339     ///
340     /// ```rust
341     /// # use clap::{Command, Arg, ArgGroup, ErrorKind};
342     /// let result = Command::new("myprog")
343     ///     .arg(Arg::new("flag")
344     ///         .short('f'))
345     ///     .arg(Arg::new("color")
346     ///         .short('c'))
347     ///     .arg(Arg::new("debug")
348     ///         .short('d'))
349     ///     .arg(Arg::new("verb")
350     ///         .short('v'))
351     ///     .group(ArgGroup::new("req_flags")
352     ///         .args(&["flag", "color"])
353     ///         .requires_all(&["debug", "verb"]))
354     ///     .try_get_matches_from(vec!["myprog", "-c", "-d"]);
355     /// // because we used an arg from the group, and the group requires "-d" and "-v" to be used,
356     /// // yet we only used "-d" it's an error
357     /// assert!(result.is_err());
358     /// let err = result.unwrap_err();
359     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
360     /// ```
361     /// [required group]: ArgGroup::required()
362     /// [argument requirement rules]: crate::Arg::requires_all()
363     #[must_use]
requires_all(mut self, ns: &[&'help str]) -> Self364     pub fn requires_all(mut self, ns: &[&'help str]) -> Self {
365         for n in ns {
366             self = self.requires(n);
367         }
368         self
369     }
370 
371     /// Specify an argument or group that must **not** be present when this group is.
372     ///
373     /// Exclusion (aka conflict) rules function just like [argument exclusion rules], you can name
374     /// other arguments or groups that must *not* be present when one of the arguments from this
375     /// group are used.
376     ///
377     /// **NOTE:** The name provided may be an argument, or group name
378     ///
379     /// # Examples
380     ///
381     /// ```rust
382     /// # use clap::{Command, Arg, ArgGroup, ErrorKind};
383     /// let result = Command::new("myprog")
384     ///     .arg(Arg::new("flag")
385     ///         .short('f'))
386     ///     .arg(Arg::new("color")
387     ///         .short('c'))
388     ///     .arg(Arg::new("debug")
389     ///         .short('d'))
390     ///     .group(ArgGroup::new("req_flags")
391     ///         .args(&["flag", "color"])
392     ///         .conflicts_with("debug"))
393     ///     .try_get_matches_from(vec!["myprog", "-c", "-d"]);
394     /// // because we used an arg from the group, and the group conflicts with "-d", it's an error
395     /// assert!(result.is_err());
396     /// let err = result.unwrap_err();
397     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
398     /// ```
399     /// [argument exclusion rules]: crate::Arg::conflicts_with()
400     #[must_use]
conflicts_with<T: Key>(mut self, id: T) -> Self401     pub fn conflicts_with<T: Key>(mut self, id: T) -> Self {
402         self.conflicts.push(id.into());
403         self
404     }
405 
406     /// Specify arguments or groups that must **not** be present when this group is.
407     ///
408     /// Exclusion rules function just like [argument exclusion rules], you can name other arguments
409     /// or groups that must *not* be present when one of the arguments from this group are used.
410     ///
411     /// **NOTE:** The names provided may be an argument, or group name
412     ///
413     /// # Examples
414     ///
415     /// ```rust
416     /// # use clap::{Command, Arg, ArgGroup, ErrorKind};
417     /// let result = Command::new("myprog")
418     ///     .arg(Arg::new("flag")
419     ///         .short('f'))
420     ///     .arg(Arg::new("color")
421     ///         .short('c'))
422     ///     .arg(Arg::new("debug")
423     ///         .short('d'))
424     ///     .arg(Arg::new("verb")
425     ///         .short('v'))
426     ///     .group(ArgGroup::new("req_flags")
427     ///         .args(&["flag", "color"])
428     ///         .conflicts_with_all(&["debug", "verb"]))
429     ///     .try_get_matches_from(vec!["myprog", "-c", "-v"]);
430     /// // because we used an arg from the group, and the group conflicts with either "-v" or "-d"
431     /// // it's an error
432     /// assert!(result.is_err());
433     /// let err = result.unwrap_err();
434     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
435     /// ```
436     ///
437     /// [argument exclusion rules]: crate::Arg::conflicts_with_all()
438     #[must_use]
conflicts_with_all(mut self, ns: &[&'help str]) -> Self439     pub fn conflicts_with_all(mut self, ns: &[&'help str]) -> Self {
440         for n in ns {
441             self = self.conflicts_with(n);
442         }
443         self
444     }
445 
446     /// Deprecated, replaced with [`ArgGroup::new`]
447     #[cfg_attr(
448         feature = "deprecated",
449         deprecated(since = "3.0.0", note = "Replaced with `ArgGroup::new`")
450     )]
451     #[doc(hidden)]
with_name<S: Into<&'help str>>(n: S) -> Self452     pub fn with_name<S: Into<&'help str>>(n: S) -> Self {
453         Self::new(n)
454     }
455 
456     /// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case?
457     #[cfg(feature = "yaml")]
458     #[cfg_attr(
459         feature = "deprecated",
460         deprecated(
461             since = "3.0.0",
462             note = "Maybe clap::Parser would fit your use case? (Issue #3087)"
463         )
464     )]
465     #[doc(hidden)]
from_yaml(yaml: &'help Yaml) -> Self466     pub fn from_yaml(yaml: &'help Yaml) -> Self {
467         Self::from(yaml)
468     }
469 }
470 
471 impl<'help> From<&'_ ArgGroup<'help>> for ArgGroup<'help> {
from(g: &ArgGroup<'help>) -> Self472     fn from(g: &ArgGroup<'help>) -> Self {
473         ArgGroup {
474             id: g.id.clone(),
475             name: g.name,
476             required: g.required,
477             args: g.args.clone(),
478             requires: g.requires.clone(),
479             conflicts: g.conflicts.clone(),
480             multiple: g.multiple,
481         }
482     }
483 }
484 
485 /// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case?
486 #[cfg(feature = "yaml")]
487 impl<'help> From<&'help Yaml> for ArgGroup<'help> {
488     /// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case?
from(y: &'help Yaml) -> Self489     fn from(y: &'help Yaml) -> Self {
490         let b = y.as_hash().expect("ArgGroup::from::<Yaml> expects a table");
491         // We WANT this to panic on error...so expect() is good.
492         let mut a = ArgGroup::default();
493         let group_settings = if b.len() == 1 {
494             let name_yaml = b.keys().next().expect("failed to get name");
495             let name_str = name_yaml
496                 .as_str()
497                 .expect("failed to convert arg YAML name to str");
498             a.name = name_str;
499             a.id = Id::from(&a.name);
500             b.get(name_yaml)
501                 .expect("failed to get name_str")
502                 .as_hash()
503                 .expect("failed to convert to a hash")
504         } else {
505             b
506         };
507 
508         for (k, v) in group_settings {
509             a = match k.as_str().unwrap() {
510                 "required" => a.required(v.as_bool().unwrap()),
511                 "multiple" => a.multiple(v.as_bool().unwrap()),
512                 "args" => yaml_vec_or_str!(a, v, arg),
513                 "arg" => {
514                     if let Some(ys) = v.as_str() {
515                         a = a.arg(ys);
516                     }
517                     a
518                 }
519                 "requires" => yaml_vec_or_str!(a, v, requires),
520                 "conflicts_with" => yaml_vec_or_str!(a, v, conflicts_with),
521                 "name" => {
522                     if let Some(ys) = v.as_str() {
523                         a = a.id(ys);
524                     }
525                     a
526                 }
527                 s => panic!(
528                     "Unknown ArgGroup setting '{}' in YAML file for \
529                      ArgGroup '{}'",
530                     s, a.name
531                 ),
532             }
533         }
534 
535         a
536     }
537 }
538 
539 #[cfg(test)]
540 mod test {
541     use super::ArgGroup;
542     #[cfg(feature = "yaml")]
543     use yaml_rust::YamlLoader;
544 
545     #[test]
groups()546     fn groups() {
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!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
560         let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
561         let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
562 
563         assert_eq!(g.args, args);
564         assert_eq!(g.requires, reqs);
565         assert_eq!(g.conflicts, confs);
566     }
567 
568     #[test]
test_from()569     fn test_from() {
570         let g = ArgGroup::new("test")
571             .arg("a1")
572             .arg("a4")
573             .args(&["a2", "a3"])
574             .required(true)
575             .conflicts_with("c1")
576             .conflicts_with_all(&["c2", "c3"])
577             .conflicts_with("c4")
578             .requires("r1")
579             .requires_all(&["r2", "r3"])
580             .requires("r4");
581 
582         let args = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
583         let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
584         let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
585 
586         let g2 = ArgGroup::from(&g);
587         assert_eq!(g2.args, args);
588         assert_eq!(g2.requires, reqs);
589         assert_eq!(g2.conflicts, confs);
590     }
591 
592     #[cfg(feature = "yaml")]
593     #[test]
test_yaml()594     fn test_yaml() {
595         let g_yaml = "name: test
596 args:
597 - a1
598 - a4
599 - a2
600 - a3
601 conflicts_with:
602 - c1
603 - c2
604 - c3
605 - c4
606 requires:
607 - r1
608 - r2
609 - r3
610 - r4";
611         let yaml = &YamlLoader::load_from_str(g_yaml).expect("failed to load YAML file")[0];
612         let g = ArgGroup::from(yaml);
613         let args = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
614         let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
615         let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
616         assert_eq!(g.args, args);
617         assert_eq!(g.requires, reqs);
618         assert_eq!(g.conflicts, confs);
619     }
620 
621     // This test will *fail to compile* if ArgGroup is not Send + Sync
622     #[test]
arg_group_send_sync()623     fn arg_group_send_sync() {
624         fn foo<T: Send + Sync>(_: T) {}
625         foo(ArgGroup::new("test"))
626     }
627 }
628 
629 impl Clone for ArgGroup<'_> {
clone(&self) -> Self630     fn clone(&self) -> Self {
631         ArgGroup {
632             id: self.id.clone(),
633             name: self.name,
634             required: self.required,
635             args: self.args.clone(),
636             requires: self.requires.clone(),
637             conflicts: self.conflicts.clone(),
638             multiple: self.multiple,
639         }
640     }
641 }
642