]>
Commit | Line | Data |
---|---|---|
04454e1e FG |
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.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> { | |
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 | ||
454 | impl<'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")] | |
470 | impl<'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)] | |
523 | mod 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 | |
579 | args: | |
580 | - a1 | |
581 | - a4 | |
582 | - a2 | |
583 | - a3 | |
584 | conflicts_with: | |
585 | - c1 | |
586 | - c2 | |
587 | - c3 | |
588 | - c4 | |
589 | requires: | |
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 | ||
612 | impl 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 | } |