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