mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 23:02:31 +00:00
imp(validator): Case-insensitive required_if_eq when arg is case-insensitive
This commit is contained in:
parent
a4b9bfc97b
commit
fa991754d3
5 changed files with 102 additions and 7 deletions
|
@ -1460,6 +1460,38 @@ impl<'help> Arg<'help> {
|
|||
/// // We did use --other=special so "cfg" had become required but was missing.
|
||||
/// assert!(res.is_err());
|
||||
/// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
|
||||
///
|
||||
/// let res = App::new("prog")
|
||||
/// .arg(Arg::new("cfg")
|
||||
/// .takes_value(true)
|
||||
/// .required_if_eq("other", "special")
|
||||
/// .long("config"))
|
||||
/// .arg(Arg::new("other")
|
||||
/// .long("other")
|
||||
/// .takes_value(true))
|
||||
/// .try_get_matches_from(vec![
|
||||
/// "prog", "--other", "SPECIAL"
|
||||
/// ]);
|
||||
///
|
||||
/// // By default, the comparison is case-sensitive, so "cfg" wasn't required
|
||||
/// assert!(res.is_ok());
|
||||
///
|
||||
/// let res = App::new("prog")
|
||||
/// .arg(Arg::new("cfg")
|
||||
/// .takes_value(true)
|
||||
/// .required_if_eq("other", "special")
|
||||
/// .long("config"))
|
||||
/// .arg(Arg::new("other")
|
||||
/// .long("other")
|
||||
/// .case_insensitive(true)
|
||||
/// .takes_value(true))
|
||||
/// .try_get_matches_from(vec![
|
||||
/// "prog", "--other", "SPECIAL"
|
||||
/// ]);
|
||||
///
|
||||
/// // However, case-insensitive comparisons can be enabled. This typically occurs when using Arg::possible_values().
|
||||
/// assert!(res.is_err());
|
||||
/// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
|
||||
/// ```
|
||||
/// [`Arg::requires(name)`]: Arg::requires()
|
||||
/// [Conflicting]: Arg::conflicts_with()
|
||||
|
@ -3700,8 +3732,15 @@ impl<'help> Arg<'help> {
|
|||
}
|
||||
}
|
||||
|
||||
/// When used with [`Arg::possible_values`] it allows the argument value to pass validation even
|
||||
/// if the case differs from that of the specified `possible_value`.
|
||||
/// When used with [`Arg::possible_values`] it allows the argument
|
||||
/// value to pass validation even if the case differs from that of
|
||||
/// the specified `possible_value`.
|
||||
///
|
||||
/// When other arguments are conditionally required based on the
|
||||
/// value of a case-insensitive argument, the equality check done
|
||||
/// by [`Arg::required_if_eq`], [`Arg::required_if_eq_any`], or
|
||||
/// [`Arg::required_if_eq_all`] is case-insensitive.
|
||||
///
|
||||
///
|
||||
/// **NOTE:** Setting this requires [`ArgSettings::TakesValue`]
|
||||
///
|
||||
|
|
|
@ -117,10 +117,11 @@ impl ArgMatcher {
|
|||
self.0.args.iter()
|
||||
}
|
||||
|
||||
pub(crate) fn inc_occurrence_of(&mut self, arg: &Id) {
|
||||
pub(crate) fn inc_occurrence_of(&mut self, arg: &Id, ci: bool) {
|
||||
debug!("ArgMatcher::inc_occurrence_of: arg={:?}", arg);
|
||||
let ma = self.entry(arg).or_insert(MatchedArg::new());
|
||||
ma.set_ty(ValueType::CommandLine);
|
||||
ma.set_case_insensitive(ci);
|
||||
ma.occurs += 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ pub(crate) struct MatchedArg {
|
|||
pub(crate) ty: ValueType,
|
||||
indices: Vec<usize>,
|
||||
vals: Vec<Vec<OsString>>,
|
||||
case_insensitive: bool,
|
||||
}
|
||||
|
||||
impl Default for MatchedArg {
|
||||
|
@ -37,6 +38,7 @@ impl MatchedArg {
|
|||
ty: ValueType::Unknown,
|
||||
indices: Vec::new(),
|
||||
vals: Vec::new(),
|
||||
case_insensitive: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,13 +124,25 @@ impl MatchedArg {
|
|||
}
|
||||
|
||||
pub(crate) fn contains_val(&self, val: &str) -> bool {
|
||||
self.vals_flatten()
|
||||
.any(|v| OsString::as_os_str(v) == OsStr::new(val))
|
||||
self.vals_flatten().any(|v| {
|
||||
if self.case_insensitive {
|
||||
// For rust v1.53.0 and above, can use
|
||||
// OsString.eq_ignore_ascii_case
|
||||
// (https://github.com/rust-lang/rust/pull/80193)
|
||||
v.to_string_lossy().to_lowercase() == val.to_lowercase()
|
||||
} else {
|
||||
OsString::as_os_str(v) == OsStr::new(val)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn set_ty(&mut self, ty: ValueType) {
|
||||
self.ty = ty;
|
||||
}
|
||||
|
||||
pub(crate) fn set_case_insensitive(&mut self, ci: bool) {
|
||||
self.case_insensitive = ci;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1521,10 +1521,10 @@ impl<'help, 'app> Parser<'help, 'app> {
|
|||
|
||||
/// Increase occurrence of specific argument and the grouped arg it's in.
|
||||
fn inc_occurrence_of_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>) {
|
||||
matcher.inc_occurrence_of(&arg.id);
|
||||
matcher.inc_occurrence_of(&arg.id, arg.is_set(ArgSettings::IgnoreCase));
|
||||
// Increment or create the group "args"
|
||||
for group in self.app.groups_for_arg(&arg.id) {
|
||||
matcher.inc_occurrence_of(&group);
|
||||
matcher.inc_occurrence_of(&group, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -533,6 +533,47 @@ fn required_if_val_present_fail() {
|
|||
assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn required_if_val_present_case_insensitive_pass() {
|
||||
let res = App::new("ri")
|
||||
.arg(
|
||||
Arg::new("cfg")
|
||||
.required_if_eq("extra", "Val")
|
||||
.takes_value(true)
|
||||
.long("config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("extra")
|
||||
.takes_value(true)
|
||||
.long("extra")
|
||||
.case_insensitive(true),
|
||||
)
|
||||
.try_get_matches_from(vec!["ri", "--extra", "vaL", "--config", "my.cfg"]);
|
||||
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn required_if_val_present_case_insensitive_fail() {
|
||||
let res = App::new("ri")
|
||||
.arg(
|
||||
Arg::new("cfg")
|
||||
.required_if_eq("extra", "Val")
|
||||
.takes_value(true)
|
||||
.long("config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("extra")
|
||||
.takes_value(true)
|
||||
.long("extra")
|
||||
.case_insensitive(true),
|
||||
)
|
||||
.try_get_matches_from(vec!["ri", "--extra", "vaL"]);
|
||||
|
||||
assert!(res.is_err());
|
||||
assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn required_if_all_values_present_pass() {
|
||||
let res = App::new("ri")
|
||||
|
|
Loading…
Reference in a new issue