mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 06:42:33 +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.
|
/// // We did use --other=special so "cfg" had become required but was missing.
|
||||||
/// assert!(res.is_err());
|
/// assert!(res.is_err());
|
||||||
/// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
|
/// 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()
|
/// [`Arg::requires(name)`]: Arg::requires()
|
||||||
/// [Conflicting]: Arg::conflicts_with()
|
/// [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
|
/// When used with [`Arg::possible_values`] it allows the argument
|
||||||
/// if the case differs from that of the specified `possible_value`.
|
/// 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`]
|
/// **NOTE:** Setting this requires [`ArgSettings::TakesValue`]
|
||||||
///
|
///
|
||||||
|
|
|
@ -117,10 +117,11 @@ impl ArgMatcher {
|
||||||
self.0.args.iter()
|
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);
|
debug!("ArgMatcher::inc_occurrence_of: arg={:?}", arg);
|
||||||
let ma = self.entry(arg).or_insert(MatchedArg::new());
|
let ma = self.entry(arg).or_insert(MatchedArg::new());
|
||||||
ma.set_ty(ValueType::CommandLine);
|
ma.set_ty(ValueType::CommandLine);
|
||||||
|
ma.set_case_insensitive(ci);
|
||||||
ma.occurs += 1;
|
ma.occurs += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub(crate) struct MatchedArg {
|
||||||
pub(crate) ty: ValueType,
|
pub(crate) ty: ValueType,
|
||||||
indices: Vec<usize>,
|
indices: Vec<usize>,
|
||||||
vals: Vec<Vec<OsString>>,
|
vals: Vec<Vec<OsString>>,
|
||||||
|
case_insensitive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MatchedArg {
|
impl Default for MatchedArg {
|
||||||
|
@ -37,6 +38,7 @@ impl MatchedArg {
|
||||||
ty: ValueType::Unknown,
|
ty: ValueType::Unknown,
|
||||||
indices: Vec::new(),
|
indices: Vec::new(),
|
||||||
vals: Vec::new(),
|
vals: Vec::new(),
|
||||||
|
case_insensitive: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,13 +124,25 @@ impl MatchedArg {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn contains_val(&self, val: &str) -> bool {
|
pub(crate) fn contains_val(&self, val: &str) -> bool {
|
||||||
self.vals_flatten()
|
self.vals_flatten().any(|v| {
|
||||||
.any(|v| OsString::as_os_str(v) == OsStr::new(val))
|
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) {
|
pub(crate) fn set_ty(&mut self, ty: ValueType) {
|
||||||
self.ty = ty;
|
self.ty = ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_case_insensitive(&mut self, ci: bool) {
|
||||||
|
self.case_insensitive = ci;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1521,10 +1521,10 @@ impl<'help, 'app> Parser<'help, 'app> {
|
||||||
|
|
||||||
/// Increase occurrence of specific argument and the grouped arg it's in.
|
/// Increase occurrence of specific argument and the grouped arg it's in.
|
||||||
fn inc_occurrence_of_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>) {
|
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"
|
// Increment or create the group "args"
|
||||||
for group in self.app.groups_for_arg(&arg.id) {
|
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);
|
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]
|
#[test]
|
||||||
fn required_if_all_values_present_pass() {
|
fn required_if_all_values_present_pass() {
|
||||||
let res = App::new("ri")
|
let res = App::new("ri")
|
||||||
|
|
Loading…
Reference in a new issue