1 // Internal 2 use crate::builder::IntoResettable; 3 use crate::util::Id; 4 5 /// Family of related [arguments]. 6 /// 7 /// By placing arguments in a logical group, you can create easier requirement and 8 /// exclusion rules instead of having to list each argument individually, or when you want a rule 9 /// to apply "any but not all" arguments. 10 /// 11 /// For instance, you can make an entire `ArgGroup` required. If [`ArgGroup::multiple(true)`] is 12 /// set, this means that at least one argument from that group must be present. If 13 /// [`ArgGroup::multiple(false)`] is set (the default), one and *only* one must be present. 14 /// 15 /// You can also do things such as name an entire `ArgGroup` as a [conflict] or [requirement] for 16 /// another argument, meaning any of the arguments that belong to that group will cause a failure 17 /// if present, or must be present respectively. 18 /// 19 /// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be 20 /// present out of a given set. Imagine that you had multiple arguments, and you want one of them 21 /// to be required, but making all of them required isn't feasible because perhaps they conflict 22 /// with each other. For example, lets say that you were building an application where one could 23 /// set a given version number by supplying a string with an option argument, i.e. 24 /// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number 25 /// and simply incrementing one of the three numbers. So you create three flags `--major`, 26 /// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to 27 /// specify that *at least one* of them is used. For this, you can create a group. 28 /// 29 /// Finally, you may use `ArgGroup`s to pull a value from a group of arguments when you don't care 30 /// exactly which argument was actually used at runtime. 31 /// 32 /// # Examples 33 /// 34 /// The following example demonstrates using an `ArgGroup` to ensure that one, and only one, of 35 /// the arguments from the specified group is present at runtime. 36 /// 37 /// ```rust 38 /// # use clap::{Command, arg, ArgGroup, error::ErrorKind}; 39 /// let result = Command::new("cmd") 40 /// .arg(arg!(--"set-ver" <ver> "set the version manually")) 41 /// .arg(arg!(--major "auto increase major")) 42 /// .arg(arg!(--minor "auto increase minor")) 43 /// .arg(arg!(--patch "auto increase patch")) 44 /// .group(ArgGroup::new("vers") 45 /// .args(["set-ver", "major", "minor", "patch"]) 46 /// .required(true)) 47 /// .try_get_matches_from(vec!["cmd", "--major", "--patch"]); 48 /// // Because we used two args in the group it's an error 49 /// assert!(result.is_err()); 50 /// let err = result.unwrap_err(); 51 /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); 52 /// ``` 53 /// 54 /// This next example shows a passing parse of the same scenario 55 /// ```rust 56 /// # use clap::{Command, arg, ArgGroup, Id}; 57 /// let result = Command::new("cmd") 58 /// .arg(arg!(--"set-ver" <ver> "set the version manually")) 59 /// .arg(arg!(--major "auto increase major")) 60 /// .arg(arg!(--minor "auto increase minor")) 61 /// .arg(arg!(--patch "auto increase patch")) 62 /// .group(ArgGroup::new("vers") 63 /// .args(["set-ver", "major", "minor","patch"]) 64 /// .required(true)) 65 /// .try_get_matches_from(vec!["cmd", "--major"]); 66 /// assert!(result.is_ok()); 67 /// let matches = result.unwrap(); 68 /// // We may not know which of the args was used, so we can test for the group... 69 /// assert!(matches.contains_id("vers")); 70 /// // We can also ask the group which arg was used 71 /// assert_eq!(matches 72 /// .get_one::<Id>("vers") 73 /// .expect("`vers` is required") 74 /// .as_str(), 75 /// "major" 76 /// ); 77 /// // we could also alternatively check each arg individually (not shown here) 78 /// ``` 79 /// [`ArgGroup::multiple(true)`]: ArgGroup::multiple() 80 /// 81 /// [`ArgGroup::multiple(false)`]: ArgGroup::multiple() 82 /// [arguments]: crate::Arg 83 /// [conflict]: crate::Arg::conflicts_with() 84 /// [requirement]: crate::Arg::requires() 85 #[derive(Default, Clone, Debug, PartialEq, Eq)] 86 pub struct ArgGroup { 87 pub(crate) id: Id, 88 pub(crate) args: Vec<Id>, 89 pub(crate) required: bool, 90 pub(crate) requires: Vec<Id>, 91 pub(crate) conflicts: Vec<Id>, 92 pub(crate) multiple: bool, 93 } 94 95 /// # Builder 96 impl ArgGroup { 97 /// Create a `ArgGroup` using a unique name. 98 /// 99 /// The name will be used to get values from the group or refer to the group inside of conflict 100 /// and requirement rules. 101 /// 102 /// # Examples 103 /// 104 /// ```rust 105 /// # use clap::{Command, ArgGroup}; 106 /// ArgGroup::new("config") 107 /// # ; 108 /// ``` new(id: impl Into<Id>) -> Self109 pub fn new(id: impl Into<Id>) -> Self { 110 ArgGroup::default().id(id) 111 } 112 113 /// Sets the group name. 114 /// 115 /// # Examples 116 /// 117 /// ```rust 118 /// # use clap::{Command, ArgGroup}; 119 /// ArgGroup::default().id("config") 120 /// # ; 121 /// ``` 122 #[must_use] id(mut self, id: impl Into<Id>) -> Self123 pub fn id(mut self, id: impl Into<Id>) -> Self { 124 self.id = id.into(); 125 self 126 } 127 128 /// Adds an [argument] to this group by name 129 /// 130 /// # Examples 131 /// 132 /// ```rust 133 /// # use clap::{Command, Arg, ArgGroup, ArgAction}; 134 /// let m = Command::new("myprog") 135 /// .arg(Arg::new("flag") 136 /// .short('f') 137 /// .action(ArgAction::SetTrue)) 138 /// .arg(Arg::new("color") 139 /// .short('c') 140 /// .action(ArgAction::SetTrue)) 141 /// .group(ArgGroup::new("req_flags") 142 /// .arg("flag") 143 /// .arg("color")) 144 /// .get_matches_from(vec!["myprog", "-f"]); 145 /// // maybe we don't know which of the two flags was used... 146 /// assert!(m.contains_id("req_flags")); 147 /// // but we can also check individually if needed 148 /// assert!(m.contains_id("flag")); 149 /// ``` 150 /// [argument]: crate::Arg 151 #[must_use] arg(mut self, arg_id: impl IntoResettable<Id>) -> Self152 pub fn arg(mut self, arg_id: impl IntoResettable<Id>) -> Self { 153 if let Some(arg_id) = arg_id.into_resettable().into_option() { 154 self.args.push(arg_id); 155 } else { 156 self.args.clear(); 157 } 158 self 159 } 160 161 /// Adds multiple [arguments] to this group by name 162 /// 163 /// # Examples 164 /// 165 /// ```rust 166 /// # use clap::{Command, Arg, ArgGroup, ArgAction}; 167 /// let m = Command::new("myprog") 168 /// .arg(Arg::new("flag") 169 /// .short('f') 170 /// .action(ArgAction::SetTrue)) 171 /// .arg(Arg::new("color") 172 /// .short('c') 173 /// .action(ArgAction::SetTrue)) 174 /// .group(ArgGroup::new("req_flags") 175 /// .args(["flag", "color"])) 176 /// .get_matches_from(vec!["myprog", "-f"]); 177 /// // maybe we don't know which of the two flags was used... 178 /// assert!(m.contains_id("req_flags")); 179 /// // but we can also check individually if needed 180 /// assert!(m.contains_id("flag")); 181 /// ``` 182 /// [arguments]: crate::Arg 183 #[must_use] args(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self184 pub fn args(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self { 185 for n in ns { 186 self = self.arg(n); 187 } 188 self 189 } 190 191 /// Getters for all args. It will return a vector of `Id` 192 /// 193 /// # Example 194 /// 195 /// ```rust 196 /// # use clap::{ArgGroup}; 197 /// let args: Vec<&str> = vec!["a1".into(), "a4".into()]; 198 /// let grp = ArgGroup::new("program").args(&args); 199 /// 200 /// for (pos, arg) in grp.get_args().enumerate() { 201 /// assert_eq!(*arg, args[pos]); 202 /// } 203 /// ``` get_args(&self) -> impl Iterator<Item = &Id>204 pub fn get_args(&self) -> impl Iterator<Item = &Id> { 205 self.args.iter() 206 } 207 208 /// Allows more than one of the [`Arg`]s in this group to be used. (Default: `false`) 209 /// 210 /// # Examples 211 /// 212 /// Notice in this example we use *both* the `-f` and `-c` flags which are both part of the 213 /// group 214 /// 215 /// ```rust 216 /// # use clap::{Command, Arg, ArgGroup, ArgAction}; 217 /// let m = Command::new("myprog") 218 /// .arg(Arg::new("flag") 219 /// .short('f') 220 /// .action(ArgAction::SetTrue)) 221 /// .arg(Arg::new("color") 222 /// .short('c') 223 /// .action(ArgAction::SetTrue)) 224 /// .group(ArgGroup::new("req_flags") 225 /// .args(["flag", "color"]) 226 /// .multiple(true)) 227 /// .get_matches_from(vec!["myprog", "-f", "-c"]); 228 /// // maybe we don't know which of the two flags was used... 229 /// assert!(m.contains_id("req_flags")); 230 /// ``` 231 /// In this next example, we show the default behavior (i.e. `multiple(false)) which will throw 232 /// an error if more than one of the args in the group was used. 233 /// 234 /// ```rust 235 /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; 236 /// let result = Command::new("myprog") 237 /// .arg(Arg::new("flag") 238 /// .short('f') 239 /// .action(ArgAction::SetTrue)) 240 /// .arg(Arg::new("color") 241 /// .short('c') 242 /// .action(ArgAction::SetTrue)) 243 /// .group(ArgGroup::new("req_flags") 244 /// .args(["flag", "color"])) 245 /// .try_get_matches_from(vec!["myprog", "-f", "-c"]); 246 /// // Because we used both args in the group it's an error 247 /// assert!(result.is_err()); 248 /// let err = result.unwrap_err(); 249 /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); 250 /// ``` 251 /// 252 /// [`Arg`]: crate::Arg 253 #[inline] 254 #[must_use] multiple(mut self, yes: bool) -> Self255 pub fn multiple(mut self, yes: bool) -> Self { 256 self.multiple = yes; 257 self 258 } 259 260 /// Return true if the group allows more than one of the arguments 261 /// in this group to be used. (Default: `false`) 262 /// 263 /// # Example 264 /// 265 /// ```rust 266 /// # use clap::{ArgGroup}; 267 /// let mut group = ArgGroup::new("myprog") 268 /// .args(["f", "c"]) 269 /// .multiple(true); 270 /// 271 /// assert!(group.is_multiple()); 272 /// ``` is_multiple(&mut self) -> bool273 pub fn is_multiple(&mut self) -> bool { 274 self.multiple 275 } 276 277 /// Require an argument from the group to be present when parsing. 278 /// 279 /// This is unless conflicting with another argument. A required group will be displayed in 280 /// the usage string of the application in the format `<arg|arg2|arg3>`. 281 /// 282 /// **NOTE:** This setting only applies to the current [`Command`] / [`Subcommand`]s, and not 283 /// globally. 284 /// 285 /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with 286 /// `ArgGroup::required(true)` states, "One and *only one* arg must be used from this group. 287 /// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which 288 /// states, '*At least* one arg from this group must be used. Using multiple is OK." 289 /// 290 /// # Examples 291 /// 292 /// ```rust 293 /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; 294 /// let result = Command::new("myprog") 295 /// .arg(Arg::new("flag") 296 /// .short('f') 297 /// .action(ArgAction::SetTrue)) 298 /// .arg(Arg::new("color") 299 /// .short('c') 300 /// .action(ArgAction::SetTrue)) 301 /// .group(ArgGroup::new("req_flags") 302 /// .args(["flag", "color"]) 303 /// .required(true)) 304 /// .try_get_matches_from(vec!["myprog"]); 305 /// // Because we didn't use any of the args in the group, it's an error 306 /// assert!(result.is_err()); 307 /// let err = result.unwrap_err(); 308 /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); 309 /// ``` 310 /// 311 /// [`Subcommand`]: crate::Subcommand 312 /// [`ArgGroup::multiple`]: ArgGroup::multiple() 313 /// [`Command`]: crate::Command 314 #[inline] 315 #[must_use] required(mut self, yes: bool) -> Self316 pub fn required(mut self, yes: bool) -> Self { 317 self.required = yes; 318 self 319 } 320 321 /// Specify an argument or group that must be present when this group is. 322 /// 323 /// This is not to be confused with a [required group]. Requirement rules function just like 324 /// [argument requirement rules], you can name other arguments or groups that must be present 325 /// when any one of the arguments from this group is used. 326 /// 327 /// **NOTE:** The name provided may be an argument or group name 328 /// 329 /// # Examples 330 /// 331 /// ```rust 332 /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; 333 /// let result = Command::new("myprog") 334 /// .arg(Arg::new("flag") 335 /// .short('f') 336 /// .action(ArgAction::SetTrue)) 337 /// .arg(Arg::new("color") 338 /// .short('c') 339 /// .action(ArgAction::SetTrue)) 340 /// .arg(Arg::new("debug") 341 /// .short('d') 342 /// .action(ArgAction::SetTrue)) 343 /// .group(ArgGroup::new("req_flags") 344 /// .args(["flag", "color"]) 345 /// .requires("debug")) 346 /// .try_get_matches_from(vec!["myprog", "-c"]); 347 /// // because we used an arg from the group, and the group requires "-d" to be used, it's an 348 /// // 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() 355 #[must_use] requires(mut self, id: impl IntoResettable<Id>) -> Self356 pub fn requires(mut self, id: impl IntoResettable<Id>) -> Self { 357 if let Some(id) = id.into_resettable().into_option() { 358 self.requires.push(id); 359 } else { 360 self.requires.clear(); 361 } 362 self 363 } 364 365 /// Specify arguments or groups that must be present when this group is. 366 /// 367 /// This is not to be confused with a [required group]. Requirement rules function just like 368 /// [argument requirement rules], you can name other arguments or groups that must be present 369 /// when one of the arguments from this group is used. 370 /// 371 /// **NOTE:** The names provided may be an argument or group name 372 /// 373 /// # Examples 374 /// 375 /// ```rust 376 /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; 377 /// let result = Command::new("myprog") 378 /// .arg(Arg::new("flag") 379 /// .short('f') 380 /// .action(ArgAction::SetTrue)) 381 /// .arg(Arg::new("color") 382 /// .short('c') 383 /// .action(ArgAction::SetTrue)) 384 /// .arg(Arg::new("debug") 385 /// .short('d') 386 /// .action(ArgAction::SetTrue)) 387 /// .arg(Arg::new("verb") 388 /// .short('v') 389 /// .action(ArgAction::SetTrue)) 390 /// .group(ArgGroup::new("req_flags") 391 /// .args(["flag", "color"]) 392 /// .requires_all(["debug", "verb"])) 393 /// .try_get_matches_from(vec!["myprog", "-c", "-d"]); 394 /// // because we used an arg from the group, and the group requires "-d" and "-v" to be used, 395 /// // yet we only used "-d" it's an error 396 /// assert!(result.is_err()); 397 /// let err = result.unwrap_err(); 398 /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); 399 /// ``` 400 /// [required group]: ArgGroup::required() 401 /// [argument requirement rules]: crate::Arg::requires_ifs() 402 #[must_use] requires_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self403 pub fn requires_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self { 404 for n in ns { 405 self = self.requires(n); 406 } 407 self 408 } 409 410 /// Specify an argument or group that must **not** be present when this group is. 411 /// 412 /// Exclusion (aka conflict) rules function just like [argument exclusion rules], you can name 413 /// other arguments or groups that must *not* be present when one of the arguments from this 414 /// group are used. 415 /// 416 /// **NOTE:** The name provided may be an argument, or group name 417 /// 418 /// # Examples 419 /// 420 /// ```rust 421 /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; 422 /// let result = Command::new("myprog") 423 /// .arg(Arg::new("flag") 424 /// .short('f') 425 /// .action(ArgAction::SetTrue)) 426 /// .arg(Arg::new("color") 427 /// .short('c') 428 /// .action(ArgAction::SetTrue)) 429 /// .arg(Arg::new("debug") 430 /// .short('d') 431 /// .action(ArgAction::SetTrue)) 432 /// .group(ArgGroup::new("req_flags") 433 /// .args(["flag", "color"]) 434 /// .conflicts_with("debug")) 435 /// .try_get_matches_from(vec!["myprog", "-c", "-d"]); 436 /// // because we used an arg from the group, and the group conflicts with "-d", it's an error 437 /// assert!(result.is_err()); 438 /// let err = result.unwrap_err(); 439 /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); 440 /// ``` 441 /// [argument exclusion rules]: crate::Arg::conflicts_with() 442 #[must_use] conflicts_with(mut self, id: impl IntoResettable<Id>) -> Self443 pub fn conflicts_with(mut self, id: impl IntoResettable<Id>) -> Self { 444 if let Some(id) = id.into_resettable().into_option() { 445 self.conflicts.push(id); 446 } else { 447 self.conflicts.clear(); 448 } 449 self 450 } 451 452 /// Specify arguments or groups that must **not** be present when this group is. 453 /// 454 /// Exclusion rules function just like [argument exclusion rules], you can name other arguments 455 /// or groups that must *not* be present when one of the arguments from this group are used. 456 /// 457 /// **NOTE:** The names provided may be an argument, or group name 458 /// 459 /// # Examples 460 /// 461 /// ```rust 462 /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; 463 /// let result = Command::new("myprog") 464 /// .arg(Arg::new("flag") 465 /// .short('f') 466 /// .action(ArgAction::SetTrue)) 467 /// .arg(Arg::new("color") 468 /// .short('c') 469 /// .action(ArgAction::SetTrue)) 470 /// .arg(Arg::new("debug") 471 /// .short('d') 472 /// .action(ArgAction::SetTrue)) 473 /// .arg(Arg::new("verb") 474 /// .short('v') 475 /// .action(ArgAction::SetTrue)) 476 /// .group(ArgGroup::new("req_flags") 477 /// .args(["flag", "color"]) 478 /// .conflicts_with_all(["debug", "verb"])) 479 /// .try_get_matches_from(vec!["myprog", "-c", "-v"]); 480 /// // because we used an arg from the group, and the group conflicts with either "-v" or "-d" 481 /// // it's an error 482 /// assert!(result.is_err()); 483 /// let err = result.unwrap_err(); 484 /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); 485 /// ``` 486 /// 487 /// [argument exclusion rules]: crate::Arg::conflicts_with_all() 488 #[must_use] conflicts_with_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self489 pub fn conflicts_with_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self { 490 for n in ns { 491 self = self.conflicts_with(n); 492 } 493 self 494 } 495 } 496 497 /// # Reflection 498 impl ArgGroup { 499 /// Get the name of the group 500 #[inline] get_id(&self) -> &Id501 pub fn get_id(&self) -> &Id { 502 &self.id 503 } 504 505 /// Reports whether [`ArgGroup::required`] is set 506 #[inline] is_required_set(&self) -> bool507 pub fn is_required_set(&self) -> bool { 508 self.required 509 } 510 } 511 512 impl From<&'_ ArgGroup> for ArgGroup { from(g: &ArgGroup) -> Self513 fn from(g: &ArgGroup) -> Self { 514 g.clone() 515 } 516 } 517 518 #[cfg(test)] 519 mod test { 520 use super::*; 521 522 #[test] groups()523 fn groups() { 524 let g = ArgGroup::new("test") 525 .arg("a1") 526 .arg("a4") 527 .args(["a2", "a3"]) 528 .required(true) 529 .conflicts_with("c1") 530 .conflicts_with_all(["c2", "c3"]) 531 .conflicts_with("c4") 532 .requires("r1") 533 .requires_all(["r2", "r3"]) 534 .requires("r4"); 535 536 let args: Vec<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()]; 537 let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()]; 538 let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()]; 539 540 assert_eq!(g.args, args); 541 assert_eq!(g.requires, reqs); 542 assert_eq!(g.conflicts, confs); 543 } 544 545 #[test] test_from()546 fn test_from() { 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<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()]; 560 let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()]; 561 let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()]; 562 563 let g2 = ArgGroup::from(&g); 564 assert_eq!(g2.args, args); 565 assert_eq!(g2.requires, reqs); 566 assert_eq!(g2.conflicts, confs); 567 } 568 569 // This test will *fail to compile* if ArgGroup is not Send + Sync 570 #[test] arg_group_send_sync()571 fn arg_group_send_sync() { 572 fn foo<T: Send + Sync>(_: T) {} 573 foo(ArgGroup::new("test")) 574 } 575 576 #[test] arg_group_expose_is_multiple_helper()577 fn arg_group_expose_is_multiple_helper() { 578 let args: Vec<Id> = vec!["a1".into(), "a4".into()]; 579 580 let mut grp_multiple = ArgGroup::new("test_multiple").args(&args).multiple(true); 581 assert!(grp_multiple.is_multiple()); 582 583 let mut grp_not_multiple = ArgGroup::new("test_multiple").args(&args).multiple(false); 584 assert!(!grp_not_multiple.is_multiple()); 585 } 586 587 #[test] arg_group_expose_get_args_helper()588 fn arg_group_expose_get_args_helper() { 589 let args: Vec<Id> = vec!["a1".into(), "a4".into()]; 590 let grp = ArgGroup::new("program").args(&args); 591 592 for (pos, arg) in grp.get_args().enumerate() { 593 assert_eq!(*arg, args[pos]); 594 } 595 } 596 } 597