clap/args/group.rs
1#[cfg(feature = "yaml")]
2use std::collections::BTreeMap;
3use std::fmt::{Debug, Formatter, Result};
4
5#[cfg(feature = "yaml")]
6use yaml_rust::Yaml;
7
8/// `ArgGroup`s are a family of related [arguments] and way for you to express, "Any of these
9/// arguments". 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 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, ArgGroup, ErrorKind};
41/// let result = App::new("app")
42/// .args_from_usage(
43/// "--set-ver [ver] 'set the version manually'
44/// --major 'auto increase major'
45/// --minor 'auto increase minor'
46/// --patch 'auto increase patch'")
47/// .group(ArgGroup::with_name("vers")
48/// .args(&["set-ver", "major", "minor", "patch"])
49/// .required(true))
50/// .get_matches_from_safe(vec!["app", "--major", "--patch"]);
51/// // Because we used two args in the group it's an error
52/// assert!(result.is_err());
53/// let err = result.unwrap_err();
54/// assert_eq!(err.kind, ErrorKind::ArgumentConflict);
55/// ```
56/// This next example shows a passing parse of the same scenario
57///
58/// ```rust
59/// # use clap::{App, ArgGroup};
60/// let result = App::new("app")
61/// .args_from_usage(
62/// "--set-ver [ver] 'set the version manually'
63/// --major 'auto increase major'
64/// --minor 'auto increase minor'
65/// --patch 'auto increase patch'")
66/// .group(ArgGroup::with_name("vers")
67/// .args(&["set-ver", "major", "minor","patch"])
68/// .required(true))
69/// .get_matches_from_safe(vec!["app", "--major"]);
70/// assert!(result.is_ok());
71/// let matches = result.unwrap();
72/// // We may not know which of the args was used, so we can test for the group...
73/// assert!(matches.is_present("vers"));
74/// // we could also alternatively check each arg individually (not shown here)
75/// ```
76/// [`ArgGroup::multiple(true)`]: ./struct.ArgGroup.html#method.multiple
77/// [arguments]: ./struct.Arg.html
78/// [conflict]: ./struct.Arg.html#method.conflicts_with
79/// [requirement]: ./struct.Arg.html#method.requires
80#[derive(Default)]
81pub struct ArgGroup<'a> {
82 #[doc(hidden)] pub name: &'a str,
83 #[doc(hidden)] pub args: Vec<&'a str>,
84 #[doc(hidden)] pub required: bool,
85 #[doc(hidden)] pub requires: Option<Vec<&'a str>>,
86 #[doc(hidden)] pub conflicts: Option<Vec<&'a str>>,
87 #[doc(hidden)] pub multiple: bool,
88}
89
90impl<'a> ArgGroup<'a> {
91 /// Creates a new instance of `ArgGroup` using a unique string name. The name will be used to
92 /// get values from the group or refer to the group inside of conflict and requirement rules.
93 ///
94 /// # Examples
95 ///
96 /// ```rust
97 /// # use clap::{App, ArgGroup};
98 /// ArgGroup::with_name("config")
99 /// # ;
100 /// ```
101 pub fn with_name(n: &'a str) -> Self {
102 ArgGroup {
103 name: n,
104 required: false,
105 args: vec![],
106 requires: None,
107 conflicts: None,
108 multiple: false,
109 }
110 }
111
112 /// Creates a new instance of `ArgGroup` from a .yml (YAML) file.
113 ///
114 /// # Examples
115 ///
116 /// ```ignore
117 /// # #[macro_use]
118 /// # extern crate clap;
119 /// # use clap::ArgGroup;
120 /// # fn main() {
121 /// let yml = load_yaml!("group.yml");
122 /// let ag = ArgGroup::from_yaml(yml);
123 /// # }
124 /// ```
125 #[cfg(feature = "yaml")]
126 pub fn from_yaml(y: &'a Yaml) -> ArgGroup<'a> { ArgGroup::from(y.as_hash().unwrap()) }
127
128 /// Adds an [argument] to this group by name
129 ///
130 /// # Examples
131 ///
132 /// ```rust
133 /// # use clap::{App, Arg, ArgGroup};
134 /// let m = App::new("myprog")
135 /// .arg(Arg::with_name("flag")
136 /// .short("f"))
137 /// .arg(Arg::with_name("color")
138 /// .short("c"))
139 /// .group(ArgGroup::with_name("req_flags")
140 /// .arg("flag")
141 /// .arg("color"))
142 /// .get_matches_from(vec!["myprog", "-f"]);
143 /// // maybe we don't know which of the two flags was used...
144 /// assert!(m.is_present("req_flags"));
145 /// // but we can also check individually if needed
146 /// assert!(m.is_present("flag"));
147 /// ```
148 /// [argument]: ./struct.Arg.html
149 #[cfg_attr(feature = "lints", allow(should_assert_eq))]
150 pub fn arg(mut self, n: &'a str) -> Self {
151 assert!(
152 self.name != n,
153 "ArgGroup '{}' can not have same name as arg inside it",
154 &*self.name
155 );
156 self.args.push(n);
157 self
158 }
159
160 /// Adds multiple [arguments] to this group by name
161 ///
162 /// # Examples
163 ///
164 /// ```rust
165 /// # use clap::{App, Arg, ArgGroup};
166 /// let m = App::new("myprog")
167 /// .arg(Arg::with_name("flag")
168 /// .short("f"))
169 /// .arg(Arg::with_name("color")
170 /// .short("c"))
171 /// .group(ArgGroup::with_name("req_flags")
172 /// .args(&["flag", "color"]))
173 /// .get_matches_from(vec!["myprog", "-f"]);
174 /// // maybe we don't know which of the two flags was used...
175 /// assert!(m.is_present("req_flags"));
176 /// // but we can also check individually if needed
177 /// assert!(m.is_present("flag"));
178 /// ```
179 /// [arguments]: ./struct.Arg.html
180 pub fn args(mut self, ns: &[&'a str]) -> Self {
181 for n in ns {
182 self = self.arg(n);
183 }
184 self
185 }
186
187 /// Allows more than one of the ['Arg']s in this group to be used. (Default: `false`)
188 ///
189 /// # Examples
190 ///
191 /// Notice in this example we use *both* the `-f` and `-c` flags which are both part of the
192 /// group
193 ///
194 /// ```rust
195 /// # use clap::{App, Arg, ArgGroup};
196 /// let m = App::new("myprog")
197 /// .arg(Arg::with_name("flag")
198 /// .short("f"))
199 /// .arg(Arg::with_name("color")
200 /// .short("c"))
201 /// .group(ArgGroup::with_name("req_flags")
202 /// .args(&["flag", "color"])
203 /// .multiple(true))
204 /// .get_matches_from(vec!["myprog", "-f", "-c"]);
205 /// // maybe we don't know which of the two flags was used...
206 /// assert!(m.is_present("req_flags"));
207 /// ```
208 /// In this next example, we show the default behavior (i.e. `multiple(false)) which will throw
209 /// an error if more than one of the args in the group was used.
210 ///
211 /// ```rust
212 /// # use clap::{App, Arg, ArgGroup, ErrorKind};
213 /// let result = App::new("myprog")
214 /// .arg(Arg::with_name("flag")
215 /// .short("f"))
216 /// .arg(Arg::with_name("color")
217 /// .short("c"))
218 /// .group(ArgGroup::with_name("req_flags")
219 /// .args(&["flag", "color"]))
220 /// .get_matches_from_safe(vec!["myprog", "-f", "-c"]);
221 /// // Because we used both args in the group it's an error
222 /// assert!(result.is_err());
223 /// let err = result.unwrap_err();
224 /// assert_eq!(err.kind, ErrorKind::ArgumentConflict);
225 /// ```
226 /// ['Arg']: ./struct.Arg.html
227 pub fn multiple(mut self, m: bool) -> Self {
228 self.multiple = m;
229 self
230 }
231
232 /// Sets the group as required or not. A required group will be displayed in the usage string
233 /// of the application in the format `<arg|arg2|arg3>`. A required `ArgGroup` simply states
234 /// that one argument from this group *must* be present at runtime (unless
235 /// conflicting with another argument).
236 ///
237 /// **NOTE:** This setting only applies to the current [`App`] / [`SubCommand`], and not
238 /// globally.
239 ///
240 /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with
241 /// `ArgGroup::required(true)` states, "One and *only one* arg must be used from this group.
242 /// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which
243 /// states, '*At least* one arg from this group must be used. Using multiple is OK."
244 ///
245 /// # Examples
246 ///
247 /// ```rust
248 /// # use clap::{App, Arg, ArgGroup, ErrorKind};
249 /// let result = App::new("myprog")
250 /// .arg(Arg::with_name("flag")
251 /// .short("f"))
252 /// .arg(Arg::with_name("color")
253 /// .short("c"))
254 /// .group(ArgGroup::with_name("req_flags")
255 /// .args(&["flag", "color"])
256 /// .required(true))
257 /// .get_matches_from_safe(vec!["myprog"]);
258 /// // Because we didn't use any of the args in the group, it's an error
259 /// assert!(result.is_err());
260 /// let err = result.unwrap_err();
261 /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
262 /// ```
263 /// [`App`]: ./struct.App.html
264 /// [`SubCommand`]: ./struct.SubCommand.html
265 /// [`ArgGroup::multiple`]: ./struct.ArgGroup.html#method.multiple
266 pub fn required(mut self, r: bool) -> Self {
267 self.required = r;
268 self
269 }
270
271 /// Sets the requirement rules of this group. This is not to be confused with a
272 /// [required group]. Requirement rules function just like [argument requirement rules], you
273 /// can name other arguments or groups that must be present when any one of the arguments from
274 /// this group is used.
275 ///
276 /// **NOTE:** The name provided may be an argument, or group name
277 ///
278 /// # Examples
279 ///
280 /// ```rust
281 /// # use clap::{App, Arg, ArgGroup, ErrorKind};
282 /// let result = App::new("myprog")
283 /// .arg(Arg::with_name("flag")
284 /// .short("f"))
285 /// .arg(Arg::with_name("color")
286 /// .short("c"))
287 /// .arg(Arg::with_name("debug")
288 /// .short("d"))
289 /// .group(ArgGroup::with_name("req_flags")
290 /// .args(&["flag", "color"])
291 /// .requires("debug"))
292 /// .get_matches_from_safe(vec!["myprog", "-c"]);
293 /// // because we used an arg from the group, and the group requires "-d" to be used, it's an
294 /// // error
295 /// assert!(result.is_err());
296 /// let err = result.unwrap_err();
297 /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
298 /// ```
299 /// [required group]: ./struct.ArgGroup.html#method.required
300 /// [argument requirement rules]: ./struct.Arg.html#method.requires
301 pub fn requires(mut self, n: &'a str) -> Self {
302 if let Some(ref mut reqs) = self.requires {
303 reqs.push(n);
304 } else {
305 self.requires = Some(vec![n]);
306 }
307 self
308 }
309
310 /// Sets the requirement rules of this group. This is not to be confused with a
311 /// [required group]. Requirement rules function just like [argument requirement rules], you
312 /// can name other arguments or groups that must be present when one of the arguments from this
313 /// group is used.
314 ///
315 /// **NOTE:** The names provided may be an argument, or group name
316 ///
317 /// # Examples
318 ///
319 /// ```rust
320 /// # use clap::{App, Arg, ArgGroup, ErrorKind};
321 /// let result = App::new("myprog")
322 /// .arg(Arg::with_name("flag")
323 /// .short("f"))
324 /// .arg(Arg::with_name("color")
325 /// .short("c"))
326 /// .arg(Arg::with_name("debug")
327 /// .short("d"))
328 /// .arg(Arg::with_name("verb")
329 /// .short("v"))
330 /// .group(ArgGroup::with_name("req_flags")
331 /// .args(&["flag", "color"])
332 /// .requires_all(&["debug", "verb"]))
333 /// .get_matches_from_safe(vec!["myprog", "-c", "-d"]);
334 /// // because we used an arg from the group, and the group requires "-d" and "-v" to be used,
335 /// // yet we only used "-d" it's an error
336 /// assert!(result.is_err());
337 /// let err = result.unwrap_err();
338 /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
339 /// ```
340 /// [required group]: ./struct.ArgGroup.html#method.required
341 /// [argument requirement rules]: ./struct.Arg.html#method.requires_all
342 pub fn requires_all(mut self, ns: &[&'a str]) -> Self {
343 for n in ns {
344 self = self.requires(n);
345 }
346 self
347 }
348
349 /// Sets the exclusion rules of this group. Exclusion (aka conflict) rules function just like
350 /// [argument exclusion rules], you can name other arguments or groups that must *not* be
351 /// present when one of the arguments from this group are used.
352 ///
353 /// **NOTE:** The name provided may be an argument, or group name
354 ///
355 /// # Examples
356 ///
357 /// ```rust
358 /// # use clap::{App, Arg, ArgGroup, ErrorKind};
359 /// let result = App::new("myprog")
360 /// .arg(Arg::with_name("flag")
361 /// .short("f"))
362 /// .arg(Arg::with_name("color")
363 /// .short("c"))
364 /// .arg(Arg::with_name("debug")
365 /// .short("d"))
366 /// .group(ArgGroup::with_name("req_flags")
367 /// .args(&["flag", "color"])
368 /// .conflicts_with("debug"))
369 /// .get_matches_from_safe(vec!["myprog", "-c", "-d"]);
370 /// // because we used an arg from the group, and the group conflicts with "-d", it's an error
371 /// assert!(result.is_err());
372 /// let err = result.unwrap_err();
373 /// assert_eq!(err.kind, ErrorKind::ArgumentConflict);
374 /// ```
375 /// [argument exclusion rules]: ./struct.Arg.html#method.conflicts_with
376 pub fn conflicts_with(mut self, n: &'a str) -> Self {
377 if let Some(ref mut confs) = self.conflicts {
378 confs.push(n);
379 } else {
380 self.conflicts = Some(vec![n]);
381 }
382 self
383 }
384
385 /// Sets the exclusion rules of this group. Exclusion rules function just like
386 /// [argument exclusion rules], you can name other arguments or groups that must *not* be
387 /// present when one of the arguments from this group are used.
388 ///
389 /// **NOTE:** The names provided may be an argument, or group name
390 ///
391 /// # Examples
392 ///
393 /// ```rust
394 /// # use clap::{App, Arg, ArgGroup, ErrorKind};
395 /// let result = App::new("myprog")
396 /// .arg(Arg::with_name("flag")
397 /// .short("f"))
398 /// .arg(Arg::with_name("color")
399 /// .short("c"))
400 /// .arg(Arg::with_name("debug")
401 /// .short("d"))
402 /// .arg(Arg::with_name("verb")
403 /// .short("v"))
404 /// .group(ArgGroup::with_name("req_flags")
405 /// .args(&["flag", "color"])
406 /// .conflicts_with_all(&["debug", "verb"]))
407 /// .get_matches_from_safe(vec!["myprog", "-c", "-v"]);
408 /// // because we used an arg from the group, and the group conflicts with either "-v" or "-d"
409 /// // it's an error
410 /// assert!(result.is_err());
411 /// let err = result.unwrap_err();
412 /// assert_eq!(err.kind, ErrorKind::ArgumentConflict);
413 /// ```
414 /// [argument exclusion rules]: ./struct.Arg.html#method.conflicts_with_all
415 pub fn conflicts_with_all(mut self, ns: &[&'a str]) -> Self {
416 for n in ns {
417 self = self.conflicts_with(n);
418 }
419 self
420 }
421}
422
423impl<'a> Debug for ArgGroup<'a> {
424 fn fmt(&self, f: &mut Formatter) -> Result {
425 write!(
426 f,
427 "{{\n\
428 \tname: {:?},\n\
429 \targs: {:?},\n\
430 \trequired: {:?},\n\
431 \trequires: {:?},\n\
432 \tconflicts: {:?},\n\
433 }}",
434 self.name,
435 self.args,
436 self.required,
437 self.requires,
438 self.conflicts
439 )
440 }
441}
442
443impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> {
444 fn from(g: &'z ArgGroup<'a>) -> Self {
445 ArgGroup {
446 name: g.name,
447 required: g.required,
448 args: g.args.clone(),
449 requires: g.requires.clone(),
450 conflicts: g.conflicts.clone(),
451 multiple: g.multiple,
452 }
453 }
454}
455
456#[cfg(feature = "yaml")]
457impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
458 fn from(b: &'a BTreeMap<Yaml, Yaml>) -> Self {
459 // We WANT this to panic on error...so expect() is good.
460 let mut a = ArgGroup::default();
461 let group_settings = if b.len() == 1 {
462 let name_yml = b.keys().nth(0).expect("failed to get name");
463 let name_str = name_yml
464 .as_str()
465 .expect("failed to convert arg YAML name to str");
466 a.name = name_str;
467 b.get(name_yml)
468 .expect("failed to get name_str")
469 .as_hash()
470 .expect("failed to convert to a hash")
471 } else {
472 b
473 };
474
475 for (k, v) in group_settings {
476 a = match k.as_str().unwrap() {
477 "required" => a.required(v.as_bool().unwrap()),
478 "multiple" => a.multiple(v.as_bool().unwrap()),
479 "args" => yaml_vec_or_str!(v, a, arg),
480 "arg" => {
481 if let Some(ys) = v.as_str() {
482 a = a.arg(ys);
483 }
484 a
485 }
486 "requires" => yaml_vec_or_str!(v, a, requires),
487 "conflicts_with" => yaml_vec_or_str!(v, a, conflicts_with),
488 "name" => {
489 if let Some(ys) = v.as_str() {
490 a.name = ys;
491 }
492 a
493 }
494 s => panic!(
495 "Unknown ArgGroup setting '{}' in YAML file for \
496 ArgGroup '{}'",
497 s,
498 a.name
499 ),
500 }
501 }
502
503 a
504 }
505}
506
507#[cfg(test)]
508mod test {
509 use super::ArgGroup;
510 #[cfg(feature = "yaml")]
511 use yaml_rust::YamlLoader;
512
513 #[test]
514 fn groups() {
515 let g = ArgGroup::with_name("test")
516 .arg("a1")
517 .arg("a4")
518 .args(&["a2", "a3"])
519 .required(true)
520 .conflicts_with("c1")
521 .conflicts_with_all(&["c2", "c3"])
522 .conflicts_with("c4")
523 .requires("r1")
524 .requires_all(&["r2", "r3"])
525 .requires("r4");
526
527 let args = vec!["a1", "a4", "a2", "a3"];
528 let reqs = vec!["r1", "r2", "r3", "r4"];
529 let confs = vec!["c1", "c2", "c3", "c4"];
530
531 assert_eq!(g.args, args);
532 assert_eq!(g.requires, Some(reqs));
533 assert_eq!(g.conflicts, Some(confs));
534 }
535
536 #[test]
537 fn test_debug() {
538 let g = ArgGroup::with_name("test")
539 .arg("a1")
540 .arg("a4")
541 .args(&["a2", "a3"])
542 .required(true)
543 .conflicts_with("c1")
544 .conflicts_with_all(&["c2", "c3"])
545 .conflicts_with("c4")
546 .requires("r1")
547 .requires_all(&["r2", "r3"])
548 .requires("r4");
549
550 let args = vec!["a1", "a4", "a2", "a3"];
551 let reqs = vec!["r1", "r2", "r3", "r4"];
552 let confs = vec!["c1", "c2", "c3", "c4"];
553
554 let debug_str = format!(
555 "{{\n\
556 \tname: \"test\",\n\
557 \targs: {:?},\n\
558 \trequired: {:?},\n\
559 \trequires: {:?},\n\
560 \tconflicts: {:?},\n\
561 }}",
562 args,
563 true,
564 Some(reqs),
565 Some(confs)
566 );
567 assert_eq!(&*format!("{:?}", g), &*debug_str);
568 }
569
570 #[test]
571 fn test_from() {
572 let g = ArgGroup::with_name("test")
573 .arg("a1")
574 .arg("a4")
575 .args(&["a2", "a3"])
576 .required(true)
577 .conflicts_with("c1")
578 .conflicts_with_all(&["c2", "c3"])
579 .conflicts_with("c4")
580 .requires("r1")
581 .requires_all(&["r2", "r3"])
582 .requires("r4");
583
584 let args = vec!["a1", "a4", "a2", "a3"];
585 let reqs = vec!["r1", "r2", "r3", "r4"];
586 let confs = vec!["c1", "c2", "c3", "c4"];
587
588 let g2 = ArgGroup::from(&g);
589 assert_eq!(g2.args, args);
590 assert_eq!(g2.requires, Some(reqs));
591 assert_eq!(g2.conflicts, Some(confs));
592 }
593
594 #[cfg(feature = "yaml")]
595 #[cfg_attr(feature = "yaml", test)]
596 fn test_yaml() {
597 let g_yaml = "name: test
598args:
599- a1
600- a4
601- a2
602- a3
603conflicts_with:
604- c1
605- c2
606- c3
607- c4
608requires:
609- r1
610- r2
611- r3
612- r4";
613 let yml = &YamlLoader::load_from_str(g_yaml).expect("failed to load YAML file")[0];
614 let g = ArgGroup::from_yaml(yml);
615 let args = vec!["a1", "a4", "a2", "a3"];
616 let reqs = vec!["r1", "r2", "r3", "r4"];
617 let confs = vec!["c1", "c2", "c3", "c4"];
618 assert_eq!(g.args, args);
619 assert_eq!(g.requires, Some(reqs));
620 assert_eq!(g.conflicts, Some(confs));
621 }
622}
623
624impl<'a> Clone for ArgGroup<'a> {
625 fn clone(&self) -> Self {
626 ArgGroup {
627 name: self.name,
628 required: self.required,
629 args: self.args.clone(),
630 requires: self.requires.clone(),
631 conflicts: self.conflicts.clone(),
632 multiple: self.multiple,
633 }
634 }
635}