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