diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/clap_builder/src/builder/arg_group.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/clap_builder/src/builder/arg_group.rs | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/third_party/rust/clap_builder/src/builder/arg_group.rs b/third_party/rust/clap_builder/src/builder/arg_group.rs new file mode 100644 index 0000000000..d3a37c1bfb --- /dev/null +++ b/third_party/rust/clap_builder/src/builder/arg_group.rs @@ -0,0 +1,611 @@ +// Internal +use crate::builder::IntoResettable; +use crate::util::Id; + +/// Family of related [arguments]. +/// +/// By placing arguments in a logical group, you can create easier requirement and +/// exclusion rules instead of having to list each argument individually, or when you want a rule +/// to apply "any but not all" arguments. +/// +/// For instance, you can make an entire `ArgGroup` required. If [`ArgGroup::multiple(true)`] is +/// set, this means that at least one argument from that group must be present. If +/// [`ArgGroup::multiple(false)`] is set (the default), one and *only* one must be present. +/// +/// You can also do things such as name an entire `ArgGroup` as a [conflict] or [requirement] for +/// another argument, meaning any of the arguments that belong to that group will cause a failure +/// if present, or must be present respectively. +/// +/// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be +/// present out of a given set. Imagine that you had multiple arguments, and you want one of them +/// to be required, but making all of them required isn't feasible because perhaps they conflict +/// with each other. For example, lets say that you were building an application where one could +/// set a given version number by supplying a string with an option argument, i.e. +/// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number +/// and simply incrementing one of the three numbers. So you create three flags `--major`, +/// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to +/// specify that *at least one* of them is used. For this, you can create a group. +/// +/// Finally, you may use `ArgGroup`s to pull a value from a group of arguments when you don't care +/// exactly which argument was actually used at runtime. +/// +/// # Examples +/// +/// The following example demonstrates using an `ArgGroup` to ensure that one, and only one, of +/// the arguments from the specified group is present at runtime. +/// +/// ```rust +/// # use clap_builder as clap; +/// # use clap::{Command, arg, ArgGroup, error::ErrorKind}; +/// let result = Command::new("cmd") +/// .arg(arg!(--"set-ver" <ver> "set the version manually")) +/// .arg(arg!(--major "auto increase major")) +/// .arg(arg!(--minor "auto increase minor")) +/// .arg(arg!(--patch "auto increase patch")) +/// .group(ArgGroup::new("vers") +/// .args(["set-ver", "major", "minor", "patch"]) +/// .required(true)) +/// .try_get_matches_from(vec!["cmd", "--major", "--patch"]); +/// // Because we used two args in the group it's an error +/// assert!(result.is_err()); +/// let err = result.unwrap_err(); +/// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +/// ``` +/// +/// This next example shows a passing parse of the same scenario +/// ```rust +/// # use clap_builder as clap; +/// # use clap::{Command, arg, ArgGroup, Id}; +/// let result = Command::new("cmd") +/// .arg(arg!(--"set-ver" <ver> "set the version manually")) +/// .arg(arg!(--major "auto increase major")) +/// .arg(arg!(--minor "auto increase minor")) +/// .arg(arg!(--patch "auto increase patch")) +/// .group(ArgGroup::new("vers") +/// .args(["set-ver", "major", "minor","patch"]) +/// .required(true)) +/// .try_get_matches_from(vec!["cmd", "--major"]); +/// assert!(result.is_ok()); +/// let matches = result.unwrap(); +/// // We may not know which of the args was used, so we can test for the group... +/// assert!(matches.contains_id("vers")); +/// // We can also ask the group which arg was used +/// assert_eq!(matches +/// .get_one::<Id>("vers") +/// .expect("`vers` is required") +/// .as_str(), +/// "major" +/// ); +/// // we could also alternatively check each arg individually (not shown here) +/// ``` +/// [`ArgGroup::multiple(true)`]: ArgGroup::multiple() +/// +/// [`ArgGroup::multiple(false)`]: ArgGroup::multiple() +/// [arguments]: crate::Arg +/// [conflict]: crate::Arg::conflicts_with() +/// [requirement]: crate::Arg::requires() +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct ArgGroup { + pub(crate) id: Id, + pub(crate) args: Vec<Id>, + pub(crate) required: bool, + pub(crate) requires: Vec<Id>, + pub(crate) conflicts: Vec<Id>, + pub(crate) multiple: bool, +} + +/// # Builder +impl ArgGroup { + /// Create a `ArgGroup` using a unique name. + /// + /// The name will be used to get values from the group or refer to the group inside of conflict + /// and requirement rules. + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, ArgGroup}; + /// ArgGroup::new("config") + /// # ; + /// ``` + pub fn new(id: impl Into<Id>) -> Self { + ArgGroup::default().id(id) + } + + /// Sets the group name. + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, ArgGroup}; + /// ArgGroup::default().id("config") + /// # ; + /// ``` + #[must_use] + pub fn id(mut self, id: impl Into<Id>) -> Self { + self.id = id.into(); + self + } + + /// Adds an [argument] to this group by name + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, ArgAction}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .arg("flag") + /// .arg("color")) + /// .get_matches_from(vec!["myprog", "-f"]); + /// // maybe we don't know which of the two flags was used... + /// assert!(m.contains_id("req_flags")); + /// // but we can also check individually if needed + /// assert!(m.contains_id("flag")); + /// ``` + /// [argument]: crate::Arg + #[must_use] + pub fn arg(mut self, arg_id: impl IntoResettable<Id>) -> Self { + if let Some(arg_id) = arg_id.into_resettable().into_option() { + self.args.push(arg_id); + } else { + self.args.clear(); + } + self + } + + /// Adds multiple [arguments] to this group by name + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, ArgAction}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"])) + /// .get_matches_from(vec!["myprog", "-f"]); + /// // maybe we don't know which of the two flags was used... + /// assert!(m.contains_id("req_flags")); + /// // but we can also check individually if needed + /// assert!(m.contains_id("flag")); + /// ``` + /// [arguments]: crate::Arg + #[must_use] + pub fn args(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self { + for n in ns { + self = self.arg(n); + } + self + } + + /// Getters for all args. It will return a vector of `Id` + /// + /// # Example + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{ArgGroup}; + /// let args: Vec<&str> = vec!["a1".into(), "a4".into()]; + /// let grp = ArgGroup::new("program").args(&args); + /// + /// for (pos, arg) in grp.get_args().enumerate() { + /// assert_eq!(*arg, args[pos]); + /// } + /// ``` + pub fn get_args(&self) -> impl Iterator<Item = &Id> { + self.args.iter() + } + + /// Allows more than one of the [`Arg`]s in this group to be used. (Default: `false`) + /// + /// # Examples + /// + /// Notice in this example we use *both* the `-f` and `-c` flags which are both part of the + /// group + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, ArgAction}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"]) + /// .multiple(true)) + /// .get_matches_from(vec!["myprog", "-f", "-c"]); + /// // maybe we don't know which of the two flags was used... + /// assert!(m.contains_id("req_flags")); + /// ``` + /// In this next example, we show the default behavior (i.e. `multiple(false)) which will throw + /// an error if more than one of the args in the group was used. + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; + /// let result = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"])) + /// .try_get_matches_from(vec!["myprog", "-f", "-c"]); + /// // Because we used both args in the group it's an error + /// assert!(result.is_err()); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + /// ``` + /// + /// [`Arg`]: crate::Arg + #[inline] + #[must_use] + pub fn multiple(mut self, yes: bool) -> Self { + self.multiple = yes; + self + } + + /// Return true if the group allows more than one of the arguments + /// in this group to be used. (Default: `false`) + /// + /// # Example + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{ArgGroup}; + /// let mut group = ArgGroup::new("myprog") + /// .args(["f", "c"]) + /// .multiple(true); + /// + /// assert!(group.is_multiple()); + /// ``` + pub fn is_multiple(&mut self) -> bool { + self.multiple + } + + /// Require an argument from the group to be present when parsing. + /// + /// This is unless conflicting with another argument. A required group will be displayed in + /// the usage string of the application in the format `<arg|arg2|arg3>`. + /// + /// **NOTE:** This setting only applies to the current [`Command`] / [`Subcommand`]s, and not + /// globally. + /// + /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with + /// `ArgGroup::required(true)` states, "One and *only one* arg must be used from this group. + /// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which + /// states, '*At least* one arg from this group must be used. Using multiple is OK." + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; + /// let result = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"]) + /// .required(true)) + /// .try_get_matches_from(vec!["myprog"]); + /// // Because we didn't use any of the args in the group, it's an error + /// assert!(result.is_err()); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); + /// ``` + /// + /// [`Subcommand`]: crate::Subcommand + /// [`ArgGroup::multiple`]: ArgGroup::multiple() + /// [`Command`]: crate::Command + #[inline] + #[must_use] + pub fn required(mut self, yes: bool) -> Self { + self.required = yes; + self + } + + /// Specify an argument or group that must be present when this group is. + /// + /// This is not to be confused with a [required group]. Requirement rules function just like + /// [argument requirement rules], you can name other arguments or groups that must be present + /// when any one of the arguments from this group is used. + /// + /// **NOTE:** The name provided may be an argument or group name + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; + /// let result = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("debug") + /// .short('d') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"]) + /// .requires("debug")) + /// .try_get_matches_from(vec!["myprog", "-c"]); + /// // because we used an arg from the group, and the group requires "-d" to be used, it's an + /// // error + /// assert!(result.is_err()); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); + /// ``` + /// [required group]: ArgGroup::required() + /// [argument requirement rules]: crate::Arg::requires() + #[must_use] + pub fn requires(mut self, id: impl IntoResettable<Id>) -> Self { + if let Some(id) = id.into_resettable().into_option() { + self.requires.push(id); + } else { + self.requires.clear(); + } + self + } + + /// Specify arguments or groups that must be present when this group is. + /// + /// This is not to be confused with a [required group]. Requirement rules function just like + /// [argument requirement rules], you can name other arguments or groups that must be present + /// when one of the arguments from this group is used. + /// + /// **NOTE:** The names provided may be an argument or group name + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; + /// let result = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("debug") + /// .short('d') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("verb") + /// .short('v') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"]) + /// .requires_all(["debug", "verb"])) + /// .try_get_matches_from(vec!["myprog", "-c", "-d"]); + /// // because we used an arg from the group, and the group requires "-d" and "-v" to be used, + /// // yet we only used "-d" it's an error + /// assert!(result.is_err()); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); + /// ``` + /// [required group]: ArgGroup::required() + /// [argument requirement rules]: crate::Arg::requires_ifs() + #[must_use] + pub fn requires_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self { + for n in ns { + self = self.requires(n); + } + self + } + + /// Specify an argument or group that must **not** be present when this group is. + /// + /// Exclusion (aka conflict) rules function just like [argument exclusion rules], you can name + /// other arguments or groups that must *not* be present when one of the arguments from this + /// group are used. + /// + /// **NOTE:** The name provided may be an argument, or group name + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; + /// let result = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("debug") + /// .short('d') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"]) + /// .conflicts_with("debug")) + /// .try_get_matches_from(vec!["myprog", "-c", "-d"]); + /// // because we used an arg from the group, and the group conflicts with "-d", it's an error + /// assert!(result.is_err()); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + /// ``` + /// [argument exclusion rules]: crate::Arg::conflicts_with() + #[must_use] + pub fn conflicts_with(mut self, id: impl IntoResettable<Id>) -> Self { + if let Some(id) = id.into_resettable().into_option() { + self.conflicts.push(id); + } else { + self.conflicts.clear(); + } + self + } + + /// Specify arguments or groups that must **not** be present when this group is. + /// + /// Exclusion rules function just like [argument exclusion rules], you can name other arguments + /// or groups that must *not* be present when one of the arguments from this group are used. + /// + /// **NOTE:** The names provided may be an argument, or group name + /// + /// # Examples + /// + /// ```rust + /// # use clap_builder as clap; + /// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction}; + /// let result = Command::new("myprog") + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("color") + /// .short('c') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("debug") + /// .short('d') + /// .action(ArgAction::SetTrue)) + /// .arg(Arg::new("verb") + /// .short('v') + /// .action(ArgAction::SetTrue)) + /// .group(ArgGroup::new("req_flags") + /// .args(["flag", "color"]) + /// .conflicts_with_all(["debug", "verb"])) + /// .try_get_matches_from(vec!["myprog", "-c", "-v"]); + /// // because we used an arg from the group, and the group conflicts with either "-v" or "-d" + /// // it's an error + /// assert!(result.is_err()); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + /// ``` + /// + /// [argument exclusion rules]: crate::Arg::conflicts_with_all() + #[must_use] + pub fn conflicts_with_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self { + for n in ns { + self = self.conflicts_with(n); + } + self + } +} + +/// # Reflection +impl ArgGroup { + /// Get the name of the group + #[inline] + pub fn get_id(&self) -> &Id { + &self.id + } + + /// Reports whether [`ArgGroup::required`] is set + #[inline] + pub fn is_required_set(&self) -> bool { + self.required + } +} + +impl From<&'_ ArgGroup> for ArgGroup { + fn from(g: &ArgGroup) -> Self { + g.clone() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn groups() { + let g = ArgGroup::new("test") + .arg("a1") + .arg("a4") + .args(["a2", "a3"]) + .required(true) + .conflicts_with("c1") + .conflicts_with_all(["c2", "c3"]) + .conflicts_with("c4") + .requires("r1") + .requires_all(["r2", "r3"]) + .requires("r4"); + + let args: Vec<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()]; + let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()]; + let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()]; + + assert_eq!(g.args, args); + assert_eq!(g.requires, reqs); + assert_eq!(g.conflicts, confs); + } + + #[test] + fn test_from() { + let g = ArgGroup::new("test") + .arg("a1") + .arg("a4") + .args(["a2", "a3"]) + .required(true) + .conflicts_with("c1") + .conflicts_with_all(["c2", "c3"]) + .conflicts_with("c4") + .requires("r1") + .requires_all(["r2", "r3"]) + .requires("r4"); + + let args: Vec<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()]; + let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()]; + let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()]; + + let g2 = ArgGroup::from(&g); + assert_eq!(g2.args, args); + assert_eq!(g2.requires, reqs); + assert_eq!(g2.conflicts, confs); + } + + // This test will *fail to compile* if ArgGroup is not Send + Sync + #[test] + fn arg_group_send_sync() { + fn foo<T: Send + Sync>(_: T) {} + foo(ArgGroup::new("test")) + } + + #[test] + fn arg_group_expose_is_multiple_helper() { + let args: Vec<Id> = vec!["a1".into(), "a4".into()]; + + let mut grp_multiple = ArgGroup::new("test_multiple").args(&args).multiple(true); + assert!(grp_multiple.is_multiple()); + + let mut grp_not_multiple = ArgGroup::new("test_multiple").args(&args).multiple(false); + assert!(!grp_not_multiple.is_multiple()); + } + + #[test] + fn arg_group_expose_get_args_helper() { + let args: Vec<Id> = vec!["a1".into(), "a4".into()]; + let grp = ArgGroup::new("program").args(&args); + + for (pos, arg) in grp.get_args().enumerate() { + assert_eq!(*arg, args[pos]); + } + } +} |