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