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