diff --git a/CHANGELOG.md b/CHANGELOG.md index 441377e0000..954b094c8c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Looking up a group in `ArgMatches` now returns the arg `Id`s, rather than the values. - Various `Arg`, `Command`, and `ArgGroup` calls were switched from accepting `&[]` to `[]` via `IntoIterator` - `Arg::short_aliases` and other builder functions that took `&[]` need the `&` dropped +- Changed `Arg::requires_ifs` and `Arg::default_value*_ifs*` to taking an `ArgPredicate`, removing ambiguity with `None` when accepting owned and borrowed types +- Replaced `Arg::requires_all` with `Arg::requires_ifs` now that it takes an `ArgPredicate` - *(help)* Make `DeriveDisplayOrder` the default and removed the setting. To sort help, set `next_display_order(None)` (#2808) - *(help)* Subcommand display order respects `Command::next_display_order` instead of `DeriveDisplayOrder` and using its own initial display order value (#2808) - *(env)* Parse `--help` and `--version` like any `ArgAction::SetTrue` flag (#3776) @@ -67,6 +69,7 @@ MSRV is now 1.60.0 - Changed the default type of `allow_external_subcommands` from `String` to `OsString` - `Arg::default_missing_value` now applies per occurrence rather than if a value is missing across all occurrences - `arg!(--long [value])` to accept `0..=1` per occurrence rather than across all occurrences, making it safe to use with `ArgAction::Append` +- Allow `OsStr`s for `Arg::{required_if_eq,required_if_eq_any,required_if_eq_all}` - *(assert)* Always enforce that version is specified when the `ArgAction::Version` is used - *(assert)* Add missing `#[track_caller]`s to make it easier to debug asserts - *(assert)* Ensure `overrides_with` IDs are valid diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 0be1c52d843..ffebc3d1073 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -1,13 +1,14 @@ // Std +#[cfg(feature = "env")] +use std::env; use std::{ borrow::Cow, cmp::{Ord, Ordering}, ffi::OsStr, + ffi::OsString, fmt::{self, Display, Formatter}, str, }; -#[cfg(feature = "env")] -use std::{env, ffi::OsString}; // Internal use super::{ArgFlags, ArgSettings}; @@ -58,9 +59,9 @@ pub struct Arg<'help> { pub(crate) settings: ArgFlags, pub(crate) overrides: Vec, pub(crate) groups: Vec, - pub(crate) requires: Vec<(ArgPredicate<'help>, Id)>, - pub(crate) r_ifs: Vec<(Id, &'help str)>, - pub(crate) r_ifs_all: Vec<(Id, &'help str)>, + pub(crate) requires: Vec<(ArgPredicate, Id)>, + pub(crate) r_ifs: Vec<(Id, OsString)>, + pub(crate) r_ifs_all: Vec<(Id, OsString)>, pub(crate) r_unless: Vec, pub(crate) r_unless_all: Vec, pub(crate) short: Option, @@ -72,7 +73,7 @@ pub struct Arg<'help> { pub(crate) num_vals: Option, pub(crate) val_delim: Option, pub(crate) default_vals: Vec<&'help OsStr>, - pub(crate) default_vals_ifs: Vec<(Id, ArgPredicate<'help>, Option<&'help OsStr>)>, + pub(crate) default_vals_ifs: Vec<(Id, ArgPredicate, Option<&'help OsStr>)>, pub(crate) default_missing_vals: Vec<&'help OsStr>, #[cfg(feature = "env")] pub(crate) env: Option<(&'help OsStr, Option)>, @@ -2501,9 +2502,6 @@ impl<'help> Arg<'help> { /// Specifies the value of the argument if `arg` has been used at runtime. /// - /// If `val` is set to `None`, `arg` only needs to be present. If `val` is set to `"some-val"` - /// then `arg` must be present at runtime **and** have the value `val`. - /// /// If `default` is set to `None`, `default_value` will be removed. /// /// **NOTE:** This setting is perfectly compatible with [`Arg::default_value`] but slightly @@ -2521,13 +2519,14 @@ impl<'help> Arg<'help> { /// /// ```rust /// # use clap::{Command, Arg, ArgAction}; + /// # use clap::builder::{ArgPredicate}; /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag") /// .action(ArgAction::SetTrue)) /// .arg(Arg::new("other") /// .long("other") - /// .default_value_if("flag", None, Some("default"))) + /// .default_value_if("flag", ArgPredicate::IsPresent, Some("default"))) /// .get_matches_from(vec![ /// "prog", "--flag" /// ]); @@ -2545,7 +2544,7 @@ impl<'help> Arg<'help> { /// .action(ArgAction::SetTrue)) /// .arg(Arg::new("other") /// .long("other") - /// .default_value_if("flag", Some("true"), Some("default"))) + /// .default_value_if("flag", "true", Some("default"))) /// .get_matches_from(vec![ /// "prog" /// ]); @@ -2563,7 +2562,7 @@ impl<'help> Arg<'help> { /// .long("opt")) /// .arg(Arg::new("other") /// .long("other") - /// .default_value_if("opt", Some("special"), Some("default"))) + /// .default_value_if("opt", "special", Some("default"))) /// .get_matches_from(vec![ /// "prog", "--opt", "special" /// ]); @@ -2582,7 +2581,7 @@ impl<'help> Arg<'help> { /// .long("opt")) /// .arg(Arg::new("other") /// .long("other") - /// .default_value_if("opt", Some("special"), Some("default"))) + /// .default_value_if("opt", "special", Some("default"))) /// .get_matches_from(vec![ /// "prog", "--opt", "hahaha" /// ]); @@ -2602,7 +2601,7 @@ impl<'help> Arg<'help> { /// .arg(Arg::new("other") /// .long("other") /// .default_value("default") - /// .default_value_if("flag", Some("true"), None)) + /// .default_value_if("flag", "true", None)) /// .get_matches_from(vec![ /// "prog", "--flag" /// ]); @@ -2615,10 +2614,10 @@ impl<'help> Arg<'help> { pub fn default_value_if( self, arg_id: impl Into, - val: Option<&'help str>, + val: impl Into, default: Option<&'help str>, ) -> Self { - self.default_value_if_os(arg_id, val.map(OsStr::new), default.map(OsStr::new)) + self.default_value_if_os(arg_id, val.into(), default.map(OsStr::new)) } /// Provides a conditional default value in the exact same manner as [`Arg::default_value_if`] @@ -2630,7 +2629,7 @@ impl<'help> Arg<'help> { pub fn default_value_if_os( mut self, arg_id: impl Into, - val: Option<&'help OsStr>, + val: impl Into, default: Option<&'help OsStr>, ) -> Self { self.default_vals_ifs @@ -2661,8 +2660,8 @@ impl<'help> Arg<'help> { /// .arg(Arg::new("other") /// .long("other") /// .default_value_ifs([ - /// ("flag", Some("true"), Some("default")), - /// ("opt", Some("channal"), Some("chan")), + /// ("flag", "true", Some("default")), + /// ("opt", "channal", Some("chan")), /// ])) /// .get_matches_from(vec![ /// "prog", "--opt", "channal" @@ -2682,8 +2681,8 @@ impl<'help> Arg<'help> { /// .arg(Arg::new("other") /// .long("other") /// .default_value_ifs([ - /// ("flag", Some("true"), Some("default")), - /// ("opt", Some("channal"), Some("chan")), + /// ("flag", "true", Some("default")), + /// ("opt", "channal", Some("chan")), /// ])) /// .get_matches_from(vec![ /// "prog" @@ -2697,6 +2696,7 @@ impl<'help> Arg<'help> { /// /// ```rust /// # use clap::{Command, Arg, ArgAction}; + /// # use clap::builder::ArgPredicate; /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag") @@ -2707,8 +2707,8 @@ impl<'help> Arg<'help> { /// .arg(Arg::new("other") /// .long("other") /// .default_value_ifs([ - /// ("flag", None, Some("default")), - /// ("opt", Some("channal"), Some("chan")), + /// ("flag", ArgPredicate::IsPresent, Some("default")), + /// ("opt", ArgPredicate::Equals("channal".into()), Some("chan")), /// ])) /// .get_matches_from(vec![ /// "prog", "--opt", "channal", "--flag" @@ -2721,10 +2721,10 @@ impl<'help> Arg<'help> { #[must_use] pub fn default_value_ifs( mut self, - ifs: impl IntoIterator, Option<&'help str>, Option<&'help str>)>, + ifs: impl IntoIterator, impl Into, Option<&'help str>)>, ) -> Self { for (arg, val, default) in ifs { - self = self.default_value_if_os(arg, val.map(OsStr::new), default.map(OsStr::new)); + self = self.default_value_if_os(arg, val, default.map(OsStr::new)); } self } @@ -2737,7 +2737,7 @@ impl<'help> Arg<'help> { #[must_use] pub fn default_value_ifs_os( mut self, - ifs: impl IntoIterator, Option<&'help OsStr>, Option<&'help OsStr>)>, + ifs: impl IntoIterator, impl Into, Option<&'help OsStr>)>, ) -> Self { for (arg, val, default) in ifs { self = self.default_value_if_os(arg.into(), val, default); @@ -3038,8 +3038,8 @@ impl<'help> Arg<'help> { /// [Conflicting]: Arg::conflicts_with() /// [required]: Arg::required() #[must_use] - pub fn required_if_eq(mut self, arg_id: impl Into, val: &'help str) -> Self { - self.r_ifs.push((arg_id.into(), val)); + pub fn required_if_eq(mut self, arg_id: impl Into, val: impl Into) -> Self { + self.r_ifs.push((arg_id.into(), val.into())); self } @@ -3119,10 +3119,10 @@ impl<'help> Arg<'help> { #[must_use] pub fn required_if_eq_any( mut self, - ifs: impl IntoIterator, &'help str)>, + ifs: impl IntoIterator, impl Into)>, ) -> Self { self.r_ifs - .extend(ifs.into_iter().map(|(id, val)| (id.into(), val))); + .extend(ifs.into_iter().map(|(id, val)| (id.into(), val.into()))); self } @@ -3200,17 +3200,17 @@ impl<'help> Arg<'help> { #[must_use] pub fn required_if_eq_all( mut self, - ifs: impl IntoIterator, &'help str)>, + ifs: impl IntoIterator, impl Into)>, ) -> Self { self.r_ifs_all - .extend(ifs.into_iter().map(|(id, val)| (id.into(), val))); + .extend(ifs.into_iter().map(|(id, val)| (id.into(), val.into()))); self } - /// Require another argument if this arg was present at runtime and its value equals to `val`. + /// Require another argument if this arg matches the [`ArgPredicate`] /// /// This method takes `value, another_arg` pair. At runtime, clap will check - /// if this arg (`self`) is present and its value equals to `val`. + /// if this arg (`self`) matches the [`ArgPredicate`]. /// If it does, `another_arg` will be marked as required. /// /// # Examples @@ -3263,15 +3263,15 @@ impl<'help> Arg<'help> { /// [Conflicting]: Arg::conflicts_with() /// [override]: Arg::overrides_with() #[must_use] - pub fn requires_if(mut self, val: &'help str, arg_id: impl Into) -> Self { - self.requires - .push((ArgPredicate::Equals(OsStr::new(val)), arg_id.into())); + pub fn requires_if(mut self, val: impl Into, arg_id: impl Into) -> Self { + self.requires.push((val.into(), arg_id.into())); self } /// Allows multiple conditional requirements. /// - /// The requirement will only become valid if this arg's value equals `val`. + /// The requirement will only become valid if this arg's value matches the + /// [`ArgPredicate`]. /// /// # Examples /// @@ -3310,66 +3310,19 @@ impl<'help> Arg<'help> { /// assert!(res.is_err()); // We used --config=special.conf so --option is required /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` - /// [`Arg::requires(name)`]: Arg::requires() - /// [Conflicting]: Arg::conflicts_with() - /// [override]: Arg::overrides_with() - #[must_use] - pub fn requires_ifs( - mut self, - ifs: impl IntoIterator)>, - ) -> Self { - self.requires.extend( - ifs.into_iter() - .map(|(val, arg)| (ArgPredicate::Equals(OsStr::new(val)), arg.into())), - ); - self - } - - /// Require these arguments names when this one is present - /// - /// i.e. when using this argument, the following arguments *must* be present. - /// - /// **NOTE:** [Conflicting] rules and [override] rules take precedence over being required - /// by default. - /// - /// # Examples - /// - /// ```rust - /// # use clap::Arg; - /// Arg::new("config") - /// .requires_all(["input", "output"]) - /// # ; - /// ``` /// - /// Setting `Arg::requires_all([arg, arg2])` requires that all the arguments be used at - /// runtime if the defining argument is used. If the defining argument isn't used, the other - /// argument isn't required + /// Setting `Arg::requires_ifs` with [`ArgPredicate::IsPresent`] and *not* supplying all the + /// arguments is an error. /// /// ```rust - /// # use clap::{Command, Arg, ArgAction}; + /// # use clap::{Command, Arg, error::ErrorKind, ArgAction, builder::ArgPredicate}; /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .action(ArgAction::Set) - /// .requires("input") - /// .long("config")) - /// .arg(Arg::new("input")) - /// .arg(Arg::new("output")) - /// .try_get_matches_from(vec![ - /// "prog" - /// ]); - /// - /// assert!(res.is_ok()); // We didn't use cfg, so input and output weren't required - /// ``` - /// - /// Setting `Arg::requires_all([arg, arg2])` and *not* supplying all the arguments is an - /// error. - /// - /// ```rust - /// # use clap::{Command, Arg, error::ErrorKind, ArgAction}; - /// let res = Command::new("prog") - /// .arg(Arg::new("cfg") - /// .action(ArgAction::Set) - /// .requires_all(["input", "output"]) + /// .requires_ifs([ + /// (ArgPredicate::IsPresent, "input"), + /// (ArgPredicate::IsPresent, "output"), + /// ]) /// .long("config")) /// .arg(Arg::new("input")) /// .arg(Arg::new("output")) @@ -3381,15 +3334,17 @@ impl<'help> Arg<'help> { /// // We didn't use output /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` + /// + /// [`Arg::requires(name)`]: Arg::requires() /// [Conflicting]: Arg::conflicts_with() /// [override]: Arg::overrides_with() #[must_use] - pub fn requires_all(mut self, names: impl IntoIterator>) -> Self { - self.requires.extend( - names - .into_iter() - .map(|s| (ArgPredicate::IsPresent, s.into())), - ); + pub fn requires_ifs( + mut self, + ifs: impl IntoIterator, impl Into)>, + ) -> Self { + self.requires + .extend(ifs.into_iter().map(|(val, arg)| (val.into(), arg.into()))); self } diff --git a/src/builder/arg_group.rs b/src/builder/arg_group.rs index 64efeb952c0..32dbc97e8a8 100644 --- a/src/builder/arg_group.rs +++ b/src/builder/arg_group.rs @@ -355,7 +355,7 @@ impl ArgGroup { /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [required group]: ArgGroup::required() - /// [argument requirement rules]: crate::Arg::requires_all() + /// [argument requirement rules]: crate::Arg::requires_ifs() #[must_use] pub fn requires_all(mut self, ns: impl IntoIterator>) -> Self { for n in ns { diff --git a/src/builder/arg_predicate.rs b/src/builder/arg_predicate.rs index 58eb5494c03..34768659cce 100644 --- a/src/builder/arg_predicate.rs +++ b/src/builder/arg_predicate.rs @@ -1,14 +1,18 @@ -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum ArgPredicate<'help> { +use crate::OsStr; + +/// Operations to perform on argument values +/// +/// These do not apply to [`ValueSource::DefaultValue`][crate::parser::ValueSource::DefaultValue] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ArgPredicate { + /// Is the argument present? IsPresent, - Equals(&'help std::ffi::OsStr), + /// Does the argument match the specified value? + Equals(OsStr), } -impl<'help> From> for ArgPredicate<'help> { - fn from(other: Option<&'help std::ffi::OsStr>) -> Self { - match other { - Some(other) => Self::Equals(other), - None => Self::IsPresent, - } +impl> From for ArgPredicate { + fn from(other: S) -> Self { + Self::Equals(other.into()) } } diff --git a/src/builder/command.rs b/src/builder/command.rs index cf657857db3..925c2995387 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -4333,7 +4333,7 @@ impl<'help> Command<'help> { pub(crate) fn unroll_arg_requires(&self, func: F, arg: &Id) -> Vec where - F: Fn(&(ArgPredicate<'_>, Id)) -> Option, + F: Fn(&(ArgPredicate, Id)) -> Option, { let mut processed = vec![]; let mut r_vec = vec![arg]; diff --git a/src/builder/mod.rs b/src/builder/mod.rs index a951c6ec82d..a147f1eb259 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -21,6 +21,7 @@ mod tests; pub use action::ArgAction; pub use arg::Arg; pub use arg_group::ArgGroup; +pub use arg_predicate::ArgPredicate; pub use command::Command; pub use possible_value::PossibleValue; pub use range::ValueRange; @@ -45,5 +46,4 @@ pub use value_parser::_AnonymousValueParser; pub(crate) use action::CountType; pub(crate) use arg::render_arg_val; -pub(crate) use arg_predicate::ArgPredicate; pub(crate) use arg_settings::{ArgFlags, ArgSettings}; diff --git a/src/lib.rs b/src/lib.rs index 0059ca39338..cf87ddd0b39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,6 +109,8 @@ pub use crate::util::color::ColorChoice; #[allow(unused_imports)] pub(crate) use crate::util::color::ColorChoice; pub use crate::util::Id; +pub(crate) use crate::util::OsStr; +pub(crate) use crate::util::Str; pub use crate::derive::{Args, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum}; diff --git a/src/output/usage.rs b/src/output/usage.rs index 0235eff5ef3..db07e873582 100644 --- a/src/output/usage.rs +++ b/src/output/usage.rs @@ -359,11 +359,11 @@ impl<'help, 'cmd> Usage<'help, 'cmd> { }; for a in required.iter() { - let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option { + let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option { let required = match val { ArgPredicate::Equals(_) => { if let Some(matcher) = matcher { - matcher.check_explicit(a, *val) + matcher.check_explicit(a, val) } else { false } @@ -395,7 +395,7 @@ impl<'help, 'cmd> Usage<'help, 'cmd> { for req in unrolled_reqs.iter().chain(incls.iter()) { if let Some(arg) = self.cmd.find(req) { let is_present = matcher - .map(|m| m.check_explicit(req, ArgPredicate::IsPresent)) + .map(|m| m.check_explicit(req, &ArgPredicate::IsPresent)) .unwrap_or(false); debug!( "Usage::get_required_usage_from:iter:{:?} arg is_present={}", @@ -417,7 +417,7 @@ impl<'help, 'cmd> Usage<'help, 'cmd> { .map(|m| { group_members .iter() - .any(|arg| m.check_explicit(arg, ArgPredicate::IsPresent)) + .any(|arg| m.check_explicit(arg, &ArgPredicate::IsPresent)) }) .unwrap_or(false); debug!( diff --git a/src/parser/arg_matcher.rs b/src/parser/arg_matcher.rs index 6b68e6411b5..433b1425029 100644 --- a/src/parser/arg_matcher.rs +++ b/src/parser/arg_matcher.rs @@ -125,7 +125,7 @@ impl ArgMatcher { self.matches.subcommand_name() } - pub(crate) fn check_explicit<'a>(&self, arg: &Id, predicate: ArgPredicate<'a>) -> bool { + pub(crate) fn check_explicit(&self, arg: &Id, predicate: &ArgPredicate) -> bool { self.get(arg).map_or(false, |a| a.check_explicit(predicate)) } diff --git a/src/parser/matches/matched_arg.rs b/src/parser/matches/matched_arg.rs index 74bac9907bd..22d5b414c14 100644 --- a/src/parser/matches/matched_arg.rs +++ b/src/parser/matches/matched_arg.rs @@ -129,7 +129,7 @@ impl MatchedArg { self.vals.iter().flatten().count() == 0 } - pub(crate) fn check_explicit(&self, predicate: ArgPredicate) -> bool { + pub(crate) fn check_explicit(&self, predicate: &ArgPredicate) -> bool { if self.source == Some(ValueSource::DefaultValue) { return false; } diff --git a/src/parser/parser.rs b/src/parser/parser.rs index d2355210be5..1eea0d17e11 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -778,7 +778,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { let used: Vec = matcher .arg_ids() .filter(|arg_id| { - matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) }) .filter(|&n| { self.cmd @@ -1513,7 +1513,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { let used: Vec = matcher .arg_ids() .filter(|arg_id| { - matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) }) .filter(|n| self.cmd.find(n).map_or(true, |a| !a.is_hide_set())) .cloned() diff --git a/src/parser/validator.rs b/src/parser/validator.rs index 3936a5be083..f29804edf0c 100644 --- a/src/parser/validator.rs +++ b/src/parser/validator.rs @@ -55,7 +55,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { if !has_subcmd && self.cmd.is_arg_required_else_help_set() { let num_user_values = matcher .arg_ids() - .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| matcher.check_explicit(arg_id, &ArgPredicate::IsPresent)) .count(); if num_user_values == 0 { let message = self.cmd.write_help_err(false, Stream::Stderr)?; @@ -95,7 +95,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { for arg_id in matcher .arg_ids() - .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| matcher.check_explicit(arg_id, &ArgPredicate::IsPresent)) .filter(|arg_id| self.cmd.find(arg_id).is_some()) { debug!("Validator::validate_conflicts::iter: id={:?}", arg_id); @@ -111,7 +111,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { let args_count = matcher .arg_ids() .filter(|arg_id| { - matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) }) .count(); if args_count <= 1 { @@ -122,7 +122,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { matcher .arg_ids() .filter(|arg_id| { - matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) }) .filter_map(|name| { debug!("Validator::validate_exclusive:iter:{:?}", name); @@ -186,7 +186,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { fn build_conflict_err_usage(&self, matcher: &ArgMatcher, conflicting_keys: &[Id]) -> String { let used_filtered: Vec = matcher .arg_ids() - .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| matcher.check_explicit(arg_id, &ArgPredicate::IsPresent)) .filter(|n| { // Filter out the args we don't want to specify. self.cmd.find(n).map_or(true, |a| !a.is_hide_set()) @@ -211,12 +211,12 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { debug!("Validator::gather_requires"); for name in matcher .arg_ids() - .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| matcher.check_explicit(arg_id, &ArgPredicate::IsPresent)) { debug!("Validator::gather_requires:iter:{:?}", name); if let Some(arg) = self.cmd.find(name) { - let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option { - let required = matcher.check_explicit(&arg.id, *val); + let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option { + let required = matcher.check_explicit(&arg.id, val); required.then(|| req_arg.clone()) }; @@ -242,7 +242,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { let is_exclusive_present = matcher .arg_ids() - .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| matcher.check_explicit(arg_id, &ArgPredicate::IsPresent)) .any(|id| { self.cmd .find(id) @@ -257,7 +257,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { for arg_or_group in self .required .iter() - .filter(|r| !matcher.check_explicit(r, ArgPredicate::IsPresent)) + .filter(|r| !matcher.check_explicit(r, &ArgPredicate::IsPresent)) { debug!("Validator::validate_required:iter:aog={:?}", arg_or_group); if let Some(arg) = self.cmd.find(arg_or_group) { @@ -271,7 +271,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { .cmd .unroll_args_in_group(&group.id) .iter() - .any(|a| matcher.check_explicit(a, ArgPredicate::IsPresent)) + .any(|a| matcher.check_explicit(a, &ArgPredicate::IsPresent)) { return self.missing_required_error(matcher, vec![]); } @@ -281,19 +281,19 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { // Validate the conditionally required args for a in self.cmd.get_arguments() { for (other, val) in &a.r_ifs { - if matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) - && !matcher.check_explicit(&a.id, ArgPredicate::IsPresent) + if matcher.check_explicit(other, &ArgPredicate::Equals(val.into())) + && !matcher.check_explicit(&a.id, &ArgPredicate::IsPresent) { return self.missing_required_error(matcher, vec![a.id.clone()]); } } let match_all = a.r_ifs_all.iter().all(|(other, val)| { - matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) + matcher.check_explicit(other, &ArgPredicate::Equals(val.into())) }); if match_all && !a.r_ifs_all.is_empty() - && !matcher.check_explicit(&a.id, ArgPredicate::IsPresent) + && !matcher.check_explicit(&a.id, &ArgPredicate::IsPresent) { return self.missing_required_error(matcher, vec![a.id.clone()]); } @@ -322,7 +322,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { .get_arguments() .filter(|&a| { (!a.r_unless.is_empty() || !a.r_unless_all.is_empty()) - && !matcher.check_explicit(&a.id, ArgPredicate::IsPresent) + && !matcher.check_explicit(&a.id, &ArgPredicate::IsPresent) && self.fails_arg_required_unless(a, matcher) }) .map(|a| a.id.clone()) @@ -337,7 +337,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { // Failing a required unless means, the arg's "unless" wasn't present, and neither were they fn fails_arg_required_unless(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { debug!("Validator::fails_arg_required_unless: a={:?}", a.get_id()); - let exists = |id| matcher.check_explicit(id, ArgPredicate::IsPresent); + let exists = |id| matcher.check_explicit(id, &ArgPredicate::IsPresent); (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists)) && !a.r_unless.iter().any(exists) @@ -365,7 +365,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { let used: Vec = matcher .arg_ids() - .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| matcher.check_explicit(arg_id, &ArgPredicate::IsPresent)) .filter(|n| { // Filter out the args we don't want to specify. self.cmd.find(n).map_or(true, |a| !a.is_hide_set()) @@ -397,7 +397,7 @@ impl Conflicts { let mut conflicts = Vec::new(); for other_arg_id in matcher .arg_ids() - .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| matcher.check_explicit(arg_id, &ArgPredicate::IsPresent)) { if arg_id == other_arg_id { continue; diff --git a/src/util/id.rs b/src/util/id.rs index b1551492c6c..0827b774885 100644 --- a/src/util/id.rs +++ b/src/util/id.rs @@ -1,69 +1,67 @@ +use crate::Str; + /// [`Arg`][crate::Arg] or [`ArgGroup`][crate::ArgGroup] identifier /// /// This is used for accessing the value in [`ArgMatches`][crate::ArgMatches] or defining /// relationships between `Arg`s and `ArgGroup`s with functions like /// [`Arg::conflicts_with`][crate::Arg::conflicts_with]. #[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct Id { - name: Inner, -} +pub struct Id(Str); impl Id { pub(crate) const HELP: Self = Self::from_static_ref("help"); pub(crate) const VERSION: Self = Self::from_static_ref("version"); pub(crate) const EXTERNAL: Self = Self::from_static_ref(""); - fn from_string(name: String) -> Self { - Self { - name: Inner::Owned(name.into_boxed_str()), - } - } - - fn from_ref(name: &str) -> Self { - Self { - name: Inner::Owned(Box::from(name)), - } - } - const fn from_static_ref(name: &'static str) -> Self { - Self { - name: Inner::Static(name), - } + Self(Str::from_static_ref(name)) } /// Get the raw string of the `Id` pub fn as_str(&self) -> &str { - self.name.as_str() + self.0.as_str() } } -impl<'s> From<&'s Id> for Id { - fn from(id: &'s Id) -> Self { +impl From<&'_ Id> for Id { + fn from(id: &'_ Id) -> Self { id.clone() } } -impl From for Id { - fn from(name: String) -> Self { - Self::from_string(name) +impl From for Id { + fn from(name: Str) -> Self { + Self(name) + } +} + +impl From<&'_ Str> for Id { + fn from(name: &'_ Str) -> Self { + Self(name.into()) + } +} + +impl From for Id { + fn from(name: std::string::String) -> Self { + Self(name.into()) } } -impl From<&'_ String> for Id { - fn from(name: &'_ String) -> Self { - Self::from_ref(name.as_str()) +impl From<&'_ std::string::String> for Id { + fn from(name: &'_ std::string::String) -> Self { + Self(name.into()) } } impl From<&'static str> for Id { fn from(name: &'static str) -> Self { - Self::from_static_ref(name) + Self(name.into()) } } impl From<&'_ &'static str> for Id { fn from(name: &'_ &'static str) -> Self { - Self::from_static_ref(*name) + Self(name.into()) } } @@ -102,64 +100,23 @@ impl PartialEq for Id { } } -impl<'s> PartialEq<&'s str> for Id { +impl PartialEq<&'_ str> for Id { #[inline] fn eq(&self, other: &&str) -> bool { PartialEq::eq(self.as_str(), *other) } } -impl PartialEq for Id { +impl PartialEq for Id { #[inline] - fn eq(&self, other: &String) -> bool { + fn eq(&self, other: &Str) -> bool { PartialEq::eq(self.as_str(), other.as_str()) } } -#[derive(Clone)] -enum Inner { - Static(&'static str), - Owned(Box), -} - -impl Inner { - fn as_str(&self) -> &str { - match self { - Self::Static(s) => s, - Self::Owned(s) => s.as_ref(), - } - } -} - -impl Default for Inner { - fn default() -> Self { - Self::Static("") - } -} - -impl PartialEq for Inner { - fn eq(&self, other: &Inner) -> bool { - self.as_str() == other.as_str() - } -} - -impl PartialOrd for Inner { - fn partial_cmp(&self, other: &Self) -> Option { - self.as_str().partial_cmp(other.as_str()) - } -} - -impl Ord for Inner { - fn cmp(&self, other: &Inner) -> std::cmp::Ordering { - self.as_str().cmp(other.as_str()) - } -} - -impl Eq for Inner {} - -impl std::hash::Hash for Inner { +impl PartialEq for Id { #[inline] - fn hash(&self, state: &mut H) { - self.as_str().hash(state); + fn eq(&self, other: &std::string::String) -> bool { + PartialEq::eq(self.as_str(), other.as_str()) } } diff --git a/src/util/mod.rs b/src/util/mod.rs index ed38fbc545f..66fecae6cf2 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -4,9 +4,13 @@ mod flat_map; mod flat_set; mod graph; mod id; +mod os_str; +mod str; mod str_to_bool; pub use self::id::Id; +pub use self::os_str::OsStr; +pub use self::str::Str; pub(crate) use self::flat_map::Entry; pub(crate) use self::flat_map::FlatMap; diff --git a/src/util/os_str.rs b/src/util/os_str.rs new file mode 100644 index 00000000000..e9d7767a2ff --- /dev/null +++ b/src/util/os_str.rs @@ -0,0 +1,204 @@ +/// A UTF-8-encoded fixed string +#[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct OsStr { + name: Inner, +} + +impl OsStr { + pub(crate) fn from_string(name: std::ffi::OsString) -> Self { + Self { + name: Inner::Owned(name.into_boxed_os_str()), + } + } + + pub(crate) fn from_ref(name: &std::ffi::OsStr) -> Self { + Self { + name: Inner::Owned(Box::from(name)), + } + } + + pub(crate) const fn from_static_ref(name: &'static std::ffi::OsStr) -> Self { + Self { + name: Inner::Static(name), + } + } + + /// Get the raw string of the `OsStr` + pub fn as_os_str(&self) -> &std::ffi::OsStr { + self.name.as_os_str() + } +} + +impl From<&'_ OsStr> for OsStr { + fn from(id: &'_ OsStr) -> Self { + id.clone() + } +} + +impl From for OsStr { + fn from(name: std::ffi::OsString) -> Self { + Self::from_string(name) + } +} + +impl From<&'_ std::ffi::OsString> for OsStr { + fn from(name: &'_ std::ffi::OsString) -> Self { + Self::from_ref(name.as_os_str()) + } +} + +impl From for OsStr { + fn from(name: std::string::String) -> Self { + Self::from_string(name.into()) + } +} + +impl From<&'_ std::string::String> for OsStr { + fn from(name: &'_ std::string::String) -> Self { + Self::from_ref(name.as_str().as_ref()) + } +} + +impl From<&'static std::ffi::OsStr> for OsStr { + fn from(name: &'static std::ffi::OsStr) -> Self { + Self::from_static_ref(name) + } +} + +impl From<&'_ &'static std::ffi::OsStr> for OsStr { + fn from(name: &'_ &'static std::ffi::OsStr) -> Self { + Self::from_static_ref(*name) + } +} + +impl From<&'static str> for OsStr { + fn from(name: &'static str) -> Self { + Self::from_static_ref(name.as_ref()) + } +} + +impl From<&'_ &'static str> for OsStr { + fn from(name: &'_ &'static str) -> Self { + Self::from_static_ref((*name).as_ref()) + } +} + +impl std::fmt::Debug for OsStr { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self.as_os_str(), f) + } +} + +impl std::ops::Deref for OsStr { + type Target = std::ffi::OsStr; + + #[inline] + fn deref(&self) -> &std::ffi::OsStr { + self.as_os_str() + } +} + +impl AsRef for OsStr { + #[inline] + fn as_ref(&self) -> &std::ffi::OsStr { + self.as_os_str() + } +} + +impl AsRef for OsStr { + #[inline] + fn as_ref(&self) -> &std::path::Path { + std::path::Path::new(self) + } +} + +impl std::borrow::Borrow for OsStr { + #[inline] + fn borrow(&self) -> &std::ffi::OsStr { + self.as_os_str() + } +} + +impl PartialEq for OsStr { + #[inline] + fn eq(&self, other: &str) -> bool { + PartialEq::eq(self.as_os_str(), other) + } +} + +impl PartialEq<&'_ str> for OsStr { + #[inline] + fn eq(&self, other: &&str) -> bool { + PartialEq::eq(self.as_os_str(), *other) + } +} + +impl<'s> PartialEq<&'s std::ffi::OsStr> for OsStr { + #[inline] + fn eq(&self, other: &&std::ffi::OsStr) -> bool { + PartialEq::eq(self.as_os_str(), *other) + } +} + +impl PartialEq for OsStr { + #[inline] + fn eq(&self, other: &std::string::String) -> bool { + PartialEq::eq(self.as_os_str(), other.as_str()) + } +} + +impl PartialEq for OsStr { + #[inline] + fn eq(&self, other: &std::ffi::OsString) -> bool { + PartialEq::eq(self.as_os_str(), other.as_os_str()) + } +} + +#[derive(Clone)] +enum Inner { + Static(&'static std::ffi::OsStr), + Owned(Box), +} + +impl Inner { + fn as_os_str(&self) -> &std::ffi::OsStr { + match self { + Self::Static(s) => s, + Self::Owned(s) => s.as_ref(), + } + } +} + +impl Default for Inner { + fn default() -> Self { + Self::Static(std::ffi::OsStr::new("")) + } +} + +impl PartialEq for Inner { + fn eq(&self, other: &Inner) -> bool { + self.as_os_str() == other.as_os_str() + } +} + +impl PartialOrd for Inner { + fn partial_cmp(&self, other: &Self) -> Option { + self.as_os_str().partial_cmp(other.as_os_str()) + } +} + +impl Ord for Inner { + fn cmp(&self, other: &Inner) -> std::cmp::Ordering { + self.as_os_str().cmp(other.as_os_str()) + } +} + +impl Eq for Inner {} + +impl std::hash::Hash for Inner { + #[inline] + fn hash(&self, state: &mut H) { + self.as_os_str().hash(state); + } +} diff --git a/src/util/str.rs b/src/util/str.rs new file mode 100644 index 00000000000..ef349a96e53 --- /dev/null +++ b/src/util/str.rs @@ -0,0 +1,187 @@ +/// A UTF-8-encoded fixed string +#[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct Str { + name: Inner, +} + +impl Str { + pub(crate) fn from_string(name: std::string::String) -> Self { + Self { + name: Inner::Owned(name.into_boxed_str()), + } + } + + pub(crate) fn from_ref(name: &str) -> Self { + Self { + name: Inner::Owned(Box::from(name)), + } + } + + pub(crate) const fn from_static_ref(name: &'static str) -> Self { + Self { + name: Inner::Static(name), + } + } + + /// Get the raw string of the `Str` + pub fn as_str(&self) -> &str { + self.name.as_str() + } +} + +impl From<&'_ Str> for Str { + fn from(id: &'_ Str) -> Self { + id.clone() + } +} + +impl From for Str { + fn from(name: std::string::String) -> Self { + Self::from_string(name) + } +} + +impl From<&'_ std::string::String> for Str { + fn from(name: &'_ std::string::String) -> Self { + Self::from_ref(name.as_str()) + } +} + +impl From<&'static str> for Str { + fn from(name: &'static str) -> Self { + Self::from_static_ref(name) + } +} + +impl From<&'_ &'static str> for Str { + fn from(name: &'_ &'static str) -> Self { + Self::from_static_ref(*name) + } +} + +impl std::fmt::Display for Str { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.as_str(), f) + } +} + +impl std::fmt::Debug for Str { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self.as_str(), f) + } +} + +impl std::ops::Deref for Str { + type Target = str; + + #[inline] + fn deref(&self) -> &str { + self.as_str() + } +} + +impl AsRef for Str { + #[inline] + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl AsRef<[u8]> for Str { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl AsRef for Str { + #[inline] + fn as_ref(&self) -> &std::ffi::OsStr { + (&**self).as_ref() + } +} + +impl AsRef for Str { + #[inline] + fn as_ref(&self) -> &std::path::Path { + std::path::Path::new(self) + } +} + +impl std::borrow::Borrow for Str { + #[inline] + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl PartialEq for Str { + #[inline] + fn eq(&self, other: &str) -> bool { + PartialEq::eq(self.as_str(), other) + } +} + +impl PartialEq<&'_ str> for Str { + #[inline] + fn eq(&self, other: &&str) -> bool { + PartialEq::eq(self.as_str(), *other) + } +} + +impl PartialEq for Str { + #[inline] + fn eq(&self, other: &std::string::String) -> bool { + PartialEq::eq(self.as_str(), other.as_str()) + } +} + +#[derive(Clone)] +enum Inner { + Static(&'static str), + Owned(Box), +} + +impl Inner { + fn as_str(&self) -> &str { + match self { + Self::Static(s) => s, + Self::Owned(s) => s.as_ref(), + } + } +} + +impl Default for Inner { + fn default() -> Self { + Self::Static("") + } +} + +impl PartialEq for Inner { + fn eq(&self, other: &Inner) -> bool { + self.as_str() == other.as_str() + } +} + +impl PartialOrd for Inner { + fn partial_cmp(&self, other: &Self) -> Option { + self.as_str().partial_cmp(other.as_str()) + } +} + +impl Ord for Inner { + fn cmp(&self, other: &Inner) -> std::cmp::Ordering { + self.as_str().cmp(other.as_str()) + } +} + +impl Eq for Inner {} + +impl std::hash::Hash for Inner { + #[inline] + fn hash(&self, state: &mut H) { + self.as_str().hash(state); + } +} diff --git a/tests/builder/action.rs b/tests/builder/action.rs index 9e7326a6545..6f4bcda2bf2 100644 --- a/tests/builder/action.rs +++ b/tests/builder/action.rs @@ -1,5 +1,6 @@ #![allow(clippy::bool_assert_comparison)] +use clap::builder::ArgPredicate; use clap::Arg; use clap::ArgAction; use clap::Command; @@ -126,7 +127,7 @@ fn set_true_with_default_value_if_present() { Arg::new("mammal") .long("mammal") .action(ArgAction::SetTrue) - .default_value_if("dog", None, Some("true")), + .default_value_if("dog", ArgPredicate::IsPresent, Some("true")), ) .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); @@ -153,7 +154,7 @@ fn set_true_with_default_value_if_value() { Arg::new("mammal") .long("mammal") .action(ArgAction::SetTrue) - .default_value_if("dog", Some("true"), Some("true")), + .default_value_if("dog", "true", Some("true")), ) .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); @@ -263,7 +264,7 @@ fn set_false_with_default_value_if_present() { Arg::new("mammal") .long("mammal") .action(ArgAction::SetFalse) - .default_value_if("dog", None, Some("false")), + .default_value_if("dog", ArgPredicate::IsPresent, Some("false")), ) .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse)); @@ -290,7 +291,7 @@ fn set_false_with_default_value_if_value() { Arg::new("mammal") .long("mammal") .action(ArgAction::SetFalse) - .default_value_if("dog", Some("false"), Some("false")), + .default_value_if("dog", "false", Some("false")), ) .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse)); @@ -366,7 +367,7 @@ fn count_with_default_value_if_present() { Arg::new("mammal") .long("mammal") .action(ArgAction::Count) - .default_value_if("dog", None, Some("10")), + .default_value_if("dog", ArgPredicate::IsPresent, Some("10")), ) .arg(Arg::new("dog").long("dog").action(ArgAction::Count)); @@ -393,7 +394,7 @@ fn count_with_default_value_if_value() { Arg::new("mammal") .long("mammal") .action(ArgAction::Count) - .default_value_if("dog", Some("2"), Some("10")), + .default_value_if("dog", "2", Some("10")), ) .arg(Arg::new("dog").long("dog").action(ArgAction::Count)); diff --git a/tests/builder/default_vals.rs b/tests/builder/default_vals.rs index 03f521405ea..fc01816aea6 100644 --- a/tests/builder/default_vals.rs +++ b/tests/builder/default_vals.rs @@ -2,6 +2,7 @@ use std::ffi::OsStr; use std::ffi::OsString; use super::utils; +use clap::builder::ArgPredicate; use clap::{arg, error::ErrorKind, value_parser, Arg, ArgAction, Command}; #[test] @@ -184,7 +185,11 @@ fn osstr_positional_user_override() { fn default_if_arg_present_no_default() { let r = Command::new("df") .arg(arg!(--opt "some arg").required(true)) - .arg(arg!([arg] "some arg").default_value_if("opt", None, Some("default"))) + .arg(arg!([arg] "some arg").default_value_if( + "opt", + ArgPredicate::IsPresent, + Some("default"), + )) .try_get_matches_from(vec!["", "--opt", "some"]); assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); @@ -199,7 +204,11 @@ fn default_if_arg_present_no_default() { fn default_if_arg_present_no_default_user_override() { let r = Command::new("df") .arg(arg!(--opt "some arg").required(false)) - .arg(arg!([arg] "some arg").default_value_if("opt", None, Some("default"))) + .arg(arg!([arg] "some arg").default_value_if( + "opt", + ArgPredicate::IsPresent, + Some("default"), + )) .try_get_matches_from(vec!["", "--opt", "some", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); @@ -217,7 +226,7 @@ fn default_if_arg_present_no_arg_with_default() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", None, Some("default")), + .default_value_if("opt", ArgPredicate::IsPresent, Some("default")), ) .try_get_matches_from(vec![""]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -236,7 +245,7 @@ fn default_if_arg_present_with_default() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", None, Some("default")), + .default_value_if("opt", ArgPredicate::IsPresent, Some("default")), ) .try_get_matches_from(vec!["", "--opt", "some"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -255,7 +264,7 @@ fn default_if_arg_present_with_default_user_override() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", None, Some("default")), + .default_value_if("opt", ArgPredicate::IsPresent, Some("default")), ) .try_get_matches_from(vec!["", "--opt", "some", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -274,7 +283,7 @@ fn default_if_arg_present_no_arg_with_default_user_override() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", None, Some("default")), + .default_value_if("opt", ArgPredicate::IsPresent, Some("default")), ) .try_get_matches_from(vec!["", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -292,7 +301,7 @@ fn default_if_arg_present_no_arg_with_default_user_override() { fn default_if_arg_present_with_value_no_default() { let r = Command::new("df") .arg(arg!(--opt "some arg").required(false)) - .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), Some("default"))) + .arg(arg!([arg] "some arg").default_value_if("opt", "value", Some("default"))) .try_get_matches_from(vec!["", "--opt", "value"]); assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); @@ -307,7 +316,7 @@ fn default_if_arg_present_with_value_no_default() { fn default_if_arg_present_with_value_no_default_fail() { let r = Command::new("df") .arg(arg!(--opt "some arg").required(false)) - .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), Some("default"))) + .arg(arg!([arg] "some arg").default_value_if("opt", "value", Some("default"))) .try_get_matches_from(vec!["", "--opt", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); @@ -319,7 +328,7 @@ fn default_if_arg_present_with_value_no_default_fail() { fn default_if_arg_present_with_value_no_default_user_override() { let r = Command::new("df") .arg(arg!(--opt "some arg").required(false)) - .arg(arg!([arg] "some arg").default_value_if("opt", Some("some"), Some("default"))) + .arg(arg!([arg] "some arg").default_value_if("opt", "some", Some("default"))) .try_get_matches_from(vec!["", "--opt", "some", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); @@ -337,7 +346,7 @@ fn default_if_arg_present_with_value_no_arg_with_default() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", Some("some"), Some("default")), + .default_value_if("opt", "some", Some("default")), ) .try_get_matches_from(vec![""]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -356,7 +365,7 @@ fn default_if_arg_present_with_value_no_arg_with_default_fail() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", Some("some"), Some("default")), + .default_value_if("opt", "some", Some("default")), ) .try_get_matches_from(vec!["", "--opt", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -375,7 +384,7 @@ fn default_if_arg_present_with_value_with_default() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", Some("some"), Some("default")), + .default_value_if("opt", "some", Some("default")), ) .try_get_matches_from(vec!["", "--opt", "some"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -394,7 +403,7 @@ fn default_if_arg_present_with_value_with_default_user_override() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", Some("some"), Some("default")), + .default_value_if("opt", "some", Some("default")), ) .try_get_matches_from(vec!["", "--opt", "some", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -413,7 +422,7 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", Some("some"), Some("default")), + .default_value_if("opt", "some", Some("default")), ) .try_get_matches_from(vec!["", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -432,7 +441,7 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_if("opt", Some("some"), Some("default")), + .default_value_if("opt", "some", Some("default")), ) .try_get_matches_from(vec!["", "--opt", "value", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -450,7 +459,7 @@ fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() { fn no_default_if_arg_present_with_value_no_default() { let r = Command::new("df") .arg(arg!(--opt "some arg").required(false)) - .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), None)) + .arg(arg!([arg] "some arg").default_value_if("opt", "value", None)) .try_get_matches_from(vec!["", "--opt", "value"]); assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); @@ -464,7 +473,7 @@ fn no_default_if_arg_present_with_value_with_default() { .arg( arg!([arg] "some arg") .default_value("default") - .default_value_if("opt", Some("value"), None), + .default_value_if("opt", "value", None), ) .try_get_matches_from(vec!["", "--opt", "value"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -480,7 +489,7 @@ fn no_default_if_arg_present_with_value_with_default_user_override() { .arg( arg!([arg] "some arg") .default_value("default") - .default_value_if("opt", Some("value"), None), + .default_value_if("opt", "value", None), ) .try_get_matches_from(vec!["", "--opt", "value", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -499,7 +508,7 @@ fn no_default_if_arg_present_no_arg_with_value_with_default() { .arg( arg!([arg] "some arg") .default_value("default") - .default_value_if("opt", Some("value"), None), + .default_value_if("opt", "value", None), ) .try_get_matches_from(vec!["", "--opt", "other"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -522,8 +531,8 @@ fn default_ifs_arg_present() { arg!([arg] "some arg") .default_value("first") .default_value_ifs([ - ("opt", Some("some"), Some("default")), - ("flag", None, Some("flg")), + ("opt", ArgPredicate::from("some"), Some("default")), + ("flag", ArgPredicate::IsPresent, Some("flg")), ]), ) .try_get_matches_from(vec!["", "--flag"]); @@ -544,7 +553,10 @@ fn no_default_ifs_arg_present() { .arg( arg!([arg] "some arg") .default_value("first") - .default_value_ifs([("opt", Some("some"), Some("default")), ("flag", None, None)]), + .default_value_ifs([ + ("opt", ArgPredicate::from("some"), Some("default")), + ("flag", ArgPredicate::IsPresent, None), + ]), ) .try_get_matches_from(vec!["", "--flag"]); assert!(r.is_ok(), "{}", r.unwrap_err()); @@ -562,8 +574,8 @@ fn default_ifs_arg_present_user_override() { arg!([arg] "some arg") .default_value("first") .default_value_ifs([ - ("opt", Some("some"), Some("default")), - ("flag", None, Some("flg")), + ("opt", ArgPredicate::from("some"), Some("default")), + ("flag", ArgPredicate::IsPresent, Some("flg")), ]), ) .try_get_matches_from(vec!["", "--flag", "value"]); @@ -585,8 +597,8 @@ fn default_ifs_arg_present_order() { arg!([arg] "some arg") .default_value("first") .default_value_ifs([ - ("opt", Some("some"), Some("default")), - ("flag", None, Some("flg")), + ("opt", ArgPredicate::from("some"), Some("default")), + ("flag", ArgPredicate::IsPresent, Some("flg")), ]), ) .try_get_matches_from(vec!["", "--opt=some", "--flag"]); @@ -612,11 +624,7 @@ fn default_value_ifs_os() { Arg::new("other") .long("other") .value_parser(value_parser!(OsString)) - .default_value_ifs_os([( - "flag", - Some(OsStr::new("标记2")), - Some(OsStr::new("flag=标记2")), - )]), + .default_value_ifs_os([("flag", "标记2", Some(OsStr::new("flag=标记2")))]), ); let result = cmd.try_get_matches_from(["my_cargo", "--flag", "标记2"]); assert!(result.is_ok(), "{}", result.unwrap_err()); diff --git a/tests/builder/require.rs b/tests/builder/require.rs index 92980a5a960..4a891a34964 100644 --- a/tests/builder/require.rs +++ b/tests/builder/require.rs @@ -1,5 +1,6 @@ use super::utils; +use clap::builder::ArgPredicate; use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command}; static REQUIRE_EQUALS: &str = "error: The following required arguments were not provided: @@ -1047,7 +1048,11 @@ fn issue_1158_app() -> Command<'static> { arg!([ID] "ID") .required_unless_present("config") .conflicts_with("config") - .requires_all(["x", "y", "z"]), + .requires_ifs([ + (ArgPredicate::IsPresent, "x"), + (ArgPredicate::IsPresent, "y"), + (ArgPredicate::IsPresent, "z"), + ]), ) .arg(arg!(x: -x "X").required(false)) .arg(arg!(y: -y "Y").required(false))