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::{App, arg, ArgGroup, ErrorKind};
41 /// let result = App::new("app")
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!["app", "--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::{App, arg, ArgGroup};
59 /// let result = App::new("app")
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!["app", "--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.is_present("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::{App, 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().name(n)
113     }
114 
115     /// Sets the group name.
116     ///
117     /// # Examples
118     ///
119     /// ```rust
120     /// # use clap::{App, ArgGroup};
121     /// ArgGroup::default().name("config")
122     /// # ;
123     /// ```
124     #[must_use]
name<S: Into<&'help str>>(mut self, n: S) -> Self125     pub fn name<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     /// Adds an [argument] to this group by name
132     ///
133     /// # Examples
134     ///
135     /// ```rust
136     /// # use clap::{App, Arg, ArgGroup};
137     /// let m = App::new("myprog")
138     ///     .arg(Arg::new("flag")
139     ///         .short('f'))
140     ///     .arg(Arg::new("color")
141     ///         .short('c'))
142     ///     .group(ArgGroup::new("req_flags")
143     ///         .arg("flag")
144     ///         .arg("color"))
145     ///     .get_matches_from(vec!["myprog", "-f"]);
146     /// // maybe we don't know which of the two flags was used...
147     /// assert!(m.is_present("req_flags"));
148     /// // but we can also check individually if needed
149     /// assert!(m.is_present("flag"));
150     /// ```
151     /// [argument]: crate::Arg
152     #[must_use]
arg<T: Key>(mut self, arg_id: T) -> Self153     pub fn arg<T: Key>(mut self, arg_id: T) -> Self {
154         self.args.push(arg_id.into());
155         self
156     }
157 
158     /// Adds multiple [arguments] to this group by name
159     ///
160     /// # Examples
161     ///
162     /// ```rust
163     /// # use clap::{App, Arg, ArgGroup};
164     /// let m = App::new("myprog")
165     ///     .arg(Arg::new("flag")
166     ///         .short('f'))
167     ///     .arg(Arg::new("color")
168     ///         .short('c'))
169     ///     .group(ArgGroup::new("req_flags")
170     ///         .args(&["flag", "color"]))
171     ///     .get_matches_from(vec!["myprog", "-f"]);
172     /// // maybe we don't know which of the two flags was used...
173     /// assert!(m.is_present("req_flags"));
174     /// // but we can also check individually if needed
175     /// assert!(m.is_present("flag"));
176     /// ```
177     /// [arguments]: crate::Arg
178     #[must_use]
args<T: Key>(mut self, ns: &[T]) -> Self179     pub fn args<T: Key>(mut self, ns: &[T]) -> Self {
180         for n in ns {
181             self = self.arg(n);
182         }
183         self
184     }
185 
186     /// Allows more than one of the [`Arg`]s in this group to be used. (Default: `false`)
187     ///
188     /// # Examples
189     ///
190     /// Notice in this example we use *both* the `-f` and `-c` flags which are both part of the
191     /// group
192     ///
193     /// ```rust
194     /// # use clap::{App, Arg, ArgGroup};
195     /// let m = App::new("myprog")
196     ///     .arg(Arg::new("flag")
197     ///         .short('f'))
198     ///     .arg(Arg::new("color")
199     ///         .short('c'))
200     ///     .group(ArgGroup::new("req_flags")
201     ///         .args(&["flag", "color"])
202     ///         .multiple(true))
203     ///     .get_matches_from(vec!["myprog", "-f", "-c"]);
204     /// // maybe we don't know which of the two flags was used...
205     /// assert!(m.is_present("req_flags"));
206     /// ```
207     /// In this next example, we show the default behavior (i.e. `multiple(false)) which will throw
208     /// an error if more than one of the args in the group was used.
209     ///
210     /// ```rust
211     /// # use clap::{App, Arg, ArgGroup, ErrorKind};
212     /// let result = App::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     ///     .try_get_matches_from(vec!["myprog", "-f", "-c"]);
220     /// // Because we used both args in the group it's an error
221     /// assert!(result.is_err());
222     /// let err = result.unwrap_err();
223     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
224     /// ```
225     ///
226     /// [`Arg`]: crate::Arg
227     #[inline]
228     #[must_use]
multiple(mut self, yes: bool) -> Self229     pub fn multiple(mut self, yes: bool) -> Self {
230         self.multiple = yes;
231         self
232     }
233 
234     /// Require an argument from the group to be present when parsing.
235     ///
236     /// This is unless conflicting with another argument.  A required group will be displayed in
237     /// the usage string of the application in the format `<arg|arg2|arg3>`.
238     ///
239     /// **NOTE:** This setting only applies to the current [`App`] / [`Subcommand`]s, and not
240     /// globally.
241     ///
242     /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with
243     /// `ArgGroup::required(true)` states, "One and *only one* arg must be used from this group.
244     /// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which
245     /// states, '*At least* one arg from this group must be used. Using multiple is OK."
246     ///
247     /// **NOTE:** An argument is considered present when there is a
248     /// [`Arg::default_value`](crate::Arg::default_value)
249     ///
250     /// # Examples
251     ///
252     /// ```rust
253     /// # use clap::{App, Arg, ArgGroup, ErrorKind};
254     /// let result = App::new("myprog")
255     ///     .arg(Arg::new("flag")
256     ///         .short('f'))
257     ///     .arg(Arg::new("color")
258     ///         .short('c'))
259     ///     .group(ArgGroup::new("req_flags")
260     ///         .args(&["flag", "color"])
261     ///         .required(true))
262     ///     .try_get_matches_from(vec!["myprog"]);
263     /// // Because we didn't use any of the args in the group, it's an error
264     /// assert!(result.is_err());
265     /// let err = result.unwrap_err();
266     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
267     /// ```
268     ///
269     /// [`Subcommand`]: crate::Subcommand
270     /// [`ArgGroup::multiple`]: ArgGroup::multiple()
271     /// [`App`]: crate::App
272     #[inline]
273     #[must_use]
required(mut self, yes: bool) -> Self274     pub fn required(mut self, yes: bool) -> Self {
275         self.required = yes;
276         self
277     }
278 
279     /// Specify an argument or group that must be present when this group is.
280     ///
281     /// This is not to be confused with a [required group]. Requirement rules function just like
282     /// [argument requirement rules], you can name other arguments or groups that must be present
283     /// when any one of the arguments from this group is used.
284     ///
285     /// **NOTE:** An argument is considered present when there is a
286     /// [`Arg::default_value`](crate::Arg::default_value)
287     ///
288     /// **NOTE:** The name provided may be an argument or group name
289     ///
290     /// # Examples
291     ///
292     /// ```rust
293     /// # use clap::{App, Arg, ArgGroup, ErrorKind};
294     /// let result = App::new("myprog")
295     ///     .arg(Arg::new("flag")
296     ///         .short('f'))
297     ///     .arg(Arg::new("color")
298     ///         .short('c'))
299     ///     .arg(Arg::new("debug")
300     ///         .short('d'))
301     ///     .group(ArgGroup::new("req_flags")
302     ///         .args(&["flag", "color"])
303     ///         .requires("debug"))
304     ///     .try_get_matches_from(vec!["myprog", "-c"]);
305     /// // because we used an arg from the group, and the group requires "-d" to be used, it's an
306     /// // error
307     /// assert!(result.is_err());
308     /// let err = result.unwrap_err();
309     /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
310     /// ```
311     /// [required group]: ArgGroup::required()
312     /// [argument requirement rules]: crate::Arg::requires()
313     #[must_use]
requires<T: Key>(mut self, id: T) -> Self314     pub fn requires<T: Key>(mut self, id: T) -> Self {
315         self.requires.push(id.into());
316         self
317     }
318 
319     /// Specify arguments or groups that must be present when this group is.
320     ///
321     /// This is not to be confused with a [required group]. Requirement rules function just like
322     /// [argument requirement rules], you can name other arguments or groups that must be present
323     /// when one of the arguments from this group is used.
324     ///
325     /// **NOTE:** The names provided may be an argument or group name
326     ///
327     /// **NOTE:** An argument is considered present when there is a
328     /// [`Arg::default_value`](crate::Arg::default_value)
329     ///
330     /// # Examples
331     ///
332     /// ```rust
333     /// # use clap::{App, Arg, ArgGroup, ErrorKind};
334     /// let result = App::new("myprog")
335     ///     .arg(Arg::new("flag")
336     ///         .short('f'))
337     ///     .arg(Arg::new("color")
338     ///         .short('c'))
339     ///     .arg(Arg::new("debug")
340     ///         .short('d'))
341     ///     .arg(Arg::new("verb")
342     ///         .short('v'))
343     ///     .group(ArgGroup::new("req_flags")
344     ///         .args(&["flag", "color"])
345     ///         .requires_all(&["debug", "verb"]))
346     ///     .try_get_matches_from(vec!["myprog", "-c", "-d"]);
347     /// // because we used an arg from the group, and the group requires "-d" and "-v" to be used,
348     /// // yet we only used "-d" it's an 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_all()
355     #[must_use]
requires_all(mut self, ns: &[&'help str]) -> Self356     pub fn requires_all(mut self, ns: &[&'help str]) -> Self {
357         for n in ns {
358             self = self.requires(n);
359         }
360         self
361     }
362 
363     /// Specify an argument or group that must **not** be present when this group is.
364     ///
365     /// Exclusion (aka conflict) rules function just like [argument exclusion rules], you can name
366     /// other arguments or groups that must *not* be present when one of the arguments from this
367     /// group are used.
368     ///
369     /// **NOTE:** The name provided may be an argument, or group name
370     ///
371     /// **NOTE:** An argument is considered present when there is a
372     /// [`Arg::default_value`](crate::Arg::default_value)
373     ///
374     /// # Examples
375     ///
376     /// ```rust
377     /// # use clap::{App, Arg, ArgGroup, ErrorKind};
378     /// let result = App::new("myprog")
379     ///     .arg(Arg::new("flag")
380     ///         .short('f'))
381     ///     .arg(Arg::new("color")
382     ///         .short('c'))
383     ///     .arg(Arg::new("debug")
384     ///         .short('d'))
385     ///     .group(ArgGroup::new("req_flags")
386     ///         .args(&["flag", "color"])
387     ///         .conflicts_with("debug"))
388     ///     .try_get_matches_from(vec!["myprog", "-c", "-d"]);
389     /// // because we used an arg from the group, and the group conflicts with "-d", it's an error
390     /// assert!(result.is_err());
391     /// let err = result.unwrap_err();
392     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
393     /// ```
394     /// [argument exclusion rules]: crate::Arg::conflicts_with()
395     #[must_use]
conflicts_with<T: Key>(mut self, id: T) -> Self396     pub fn conflicts_with<T: Key>(mut self, id: T) -> Self {
397         self.conflicts.push(id.into());
398         self
399     }
400 
401     /// Specify arguments or groups that must **not** be present when this group is.
402     ///
403     /// Exclusion rules function just like [argument exclusion rules], you can name other arguments
404     /// or groups that must *not* be present when one of the arguments from this group are used.
405     ///
406     /// **NOTE:** The names provided may be an argument, or group name
407     ///
408     /// **NOTE:** An argument is considered present when there is a
409     /// [`Arg::default_value`](crate::Arg::default_value)
410     ///
411     /// # Examples
412     ///
413     /// ```rust
414     /// # use clap::{App, Arg, ArgGroup, ErrorKind};
415     /// let result = App::new("myprog")
416     ///     .arg(Arg::new("flag")
417     ///         .short('f'))
418     ///     .arg(Arg::new("color")
419     ///         .short('c'))
420     ///     .arg(Arg::new("debug")
421     ///         .short('d'))
422     ///     .arg(Arg::new("verb")
423     ///         .short('v'))
424     ///     .group(ArgGroup::new("req_flags")
425     ///         .args(&["flag", "color"])
426     ///         .conflicts_with_all(&["debug", "verb"]))
427     ///     .try_get_matches_from(vec!["myprog", "-c", "-v"]);
428     /// // because we used an arg from the group, and the group conflicts with either "-v" or "-d"
429     /// // it's an error
430     /// assert!(result.is_err());
431     /// let err = result.unwrap_err();
432     /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
433     /// ```
434     ///
435     /// [argument exclusion rules]: crate::Arg::conflicts_with_all()
436     #[must_use]
conflicts_with_all(mut self, ns: &[&'help str]) -> Self437     pub fn conflicts_with_all(mut self, ns: &[&'help str]) -> Self {
438         for n in ns {
439             self = self.conflicts_with(n);
440         }
441         self
442     }
443 
444     /// Deprecated, replaced with [`ArgGroup::new`]
445     #[deprecated(since = "3.0.0", note = "Replaced with `ArgGroup::new`")]
with_name<S: Into<&'help str>>(n: S) -> Self446     pub fn with_name<S: Into<&'help str>>(n: S) -> Self {
447         Self::new(n)
448     }
449 
450     /// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case?
451     #[cfg(feature = "yaml")]
452     #[deprecated(
453         since = "3.0.0",
454         note = "Maybe clap::Parser would fit your use case? (Issue #3087)"
455     )]
from_yaml(yaml: &'help Yaml) -> Self456     pub fn from_yaml(yaml: &'help Yaml) -> Self {
457         Self::from(yaml)
458     }
459 }
460 
461 impl<'help> From<&'_ ArgGroup<'help>> for ArgGroup<'help> {
from(g: &ArgGroup<'help>) -> Self462     fn from(g: &ArgGroup<'help>) -> Self {
463         ArgGroup {
464             id: g.id.clone(),
465             name: g.name,
466             required: g.required,
467             args: g.args.clone(),
468             requires: g.requires.clone(),
469             conflicts: g.conflicts.clone(),
470             multiple: g.multiple,
471         }
472     }
473 }
474 
475 /// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case?
476 #[cfg(feature = "yaml")]
477 impl<'help> From<&'help Yaml> for ArgGroup<'help> {
478     /// 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) -> Self479     fn from(y: &'help Yaml) -> Self {
480         let b = y.as_hash().expect("ArgGroup::from::<Yaml> expects a table");
481         // We WANT this to panic on error...so expect() is good.
482         let mut a = ArgGroup::default();
483         let group_settings = if b.len() == 1 {
484             let name_yaml = b.keys().next().expect("failed to get name");
485             let name_str = name_yaml
486                 .as_str()
487                 .expect("failed to convert arg YAML name to str");
488             a.name = name_str;
489             a.id = Id::from(&a.name);
490             b.get(name_yaml)
491                 .expect("failed to get name_str")
492                 .as_hash()
493                 .expect("failed to convert to a hash")
494         } else {
495             b
496         };
497 
498         for (k, v) in group_settings {
499             a = match k.as_str().unwrap() {
500                 "required" => a.required(v.as_bool().unwrap()),
501                 "multiple" => a.multiple(v.as_bool().unwrap()),
502                 "args" => yaml_vec_or_str!(a, v, arg),
503                 "arg" => {
504                     if let Some(ys) = v.as_str() {
505                         a = a.arg(ys);
506                     }
507                     a
508                 }
509                 "requires" => yaml_vec_or_str!(a, v, requires),
510                 "conflicts_with" => yaml_vec_or_str!(a, v, conflicts_with),
511                 "name" => {
512                     if let Some(ys) = v.as_str() {
513                         a = a.name(ys);
514                     }
515                     a
516                 }
517                 s => panic!(
518                     "Unknown ArgGroup setting '{}' in YAML file for \
519                      ArgGroup '{}'",
520                     s, a.name
521                 ),
522             }
523         }
524 
525         a
526     }
527 }
528 
529 #[cfg(test)]
530 mod test {
531     use super::ArgGroup;
532     #[cfg(feature = "yaml")]
533     use yaml_rust::YamlLoader;
534 
535     #[test]
groups()536     fn groups() {
537         let g = ArgGroup::new("test")
538             .arg("a1")
539             .arg("a4")
540             .args(&["a2", "a3"])
541             .required(true)
542             .conflicts_with("c1")
543             .conflicts_with_all(&["c2", "c3"])
544             .conflicts_with("c4")
545             .requires("r1")
546             .requires_all(&["r2", "r3"])
547             .requires("r4");
548 
549         let args = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
550         let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
551         let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
552 
553         assert_eq!(g.args, args);
554         assert_eq!(g.requires, reqs);
555         assert_eq!(g.conflicts, confs);
556     }
557 
558     #[test]
test_from()559     fn test_from() {
560         let g = ArgGroup::new("test")
561             .arg("a1")
562             .arg("a4")
563             .args(&["a2", "a3"])
564             .required(true)
565             .conflicts_with("c1")
566             .conflicts_with_all(&["c2", "c3"])
567             .conflicts_with("c4")
568             .requires("r1")
569             .requires_all(&["r2", "r3"])
570             .requires("r4");
571 
572         let args = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
573         let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
574         let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
575 
576         let g2 = ArgGroup::from(&g);
577         assert_eq!(g2.args, args);
578         assert_eq!(g2.requires, reqs);
579         assert_eq!(g2.conflicts, confs);
580     }
581 
582     #[cfg(feature = "yaml")]
583     #[test]
test_yaml()584     fn test_yaml() {
585         let g_yaml = "name: test
586 args:
587 - a1
588 - a4
589 - a2
590 - a3
591 conflicts_with:
592 - c1
593 - c2
594 - c3
595 - c4
596 requires:
597 - r1
598 - r2
599 - r3
600 - r4";
601         let yaml = &YamlLoader::load_from_str(g_yaml).expect("failed to load YAML file")[0];
602         let g = ArgGroup::from(yaml);
603         let args = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
604         let reqs = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
605         let confs = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
606         assert_eq!(g.args, args);
607         assert_eq!(g.requires, reqs);
608         assert_eq!(g.conflicts, confs);
609     }
610 
611     // This test will *fail to compile* if ArgGroup is not Send + Sync
612     #[test]
arg_group_send_sync()613     fn arg_group_send_sync() {
614         fn foo<T: Send + Sync>(_: T) {}
615         foo(ArgGroup::new("test"))
616     }
617 }
618 
619 impl Clone for ArgGroup<'_> {
clone(&self) -> Self620     fn clone(&self) -> Self {
621         ArgGroup {
622             id: self.id.clone(),
623             name: self.name,
624             required: self.required,
625             args: self.args.clone(),
626             requires: self.requires.clone(),
627             conflicts: self.conflicts.clone(),
628             multiple: self.multiple,
629         }
630     }
631 }
632