mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
api: Adds Arg::case_insensitive(bool) which allows matching Arg::possible_values without worrying about ASCII case
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`. ```rust let m = App::new("pv") .arg(Arg::with_name("option") .long("--option") .takes_value(true) .possible_value("test123") .case_insensitive(true)) .get_matches_from(vec![ "pv", "--option", "TeSt123", ]); assert!(m.value_of("option").unwrap().eq_ignore_ascii_case("test123")); ``` This setting also works when multiple values can be defined: ```rust let m = App::new("pv") .arg(Arg::with_name("option") .short("-o") .long("--option") .takes_value(true) .possible_value("test123") .possible_value("test321") .multiple(true) .case_insensitive(true)) .get_matches_from(vec![ "pv", "--option", "TeSt123", "teST123", "tESt321" ]); let matched_vals = m.values_of("option").unwrap().collect::<Vec<_>>(); assert_eq!(&*matched_vals, &["TeSt123", "teST123", "tESt321"]); ``` Closes #1118
This commit is contained in:
parent
e53c33d6b9
commit
80993357e1
5 changed files with 84 additions and 8 deletions
|
@ -1,5 +1,7 @@
|
|||
// std
|
||||
use std::fmt::Display;
|
||||
#[allow(unused_imports)]
|
||||
use std::ascii::AsciiExt;
|
||||
|
||||
// Internal
|
||||
use INTERNAL_ERROR_MSG;
|
||||
|
@ -89,7 +91,12 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
|
|||
if let Some(p_vals) = arg.possible_vals() {
|
||||
debugln!("Validator::validate_values: possible_vals={:?}", p_vals);
|
||||
let val_str = val.to_string_lossy();
|
||||
if !p_vals.contains(&&*val_str) {
|
||||
let ok = if arg.is_set(ArgSettings::CaseInsensitive) {
|
||||
p_vals.iter().any(|pv| pv.eq_ignore_ascii_case(&*val_str))
|
||||
} else {
|
||||
p_vals.contains(&&*val_str)
|
||||
};
|
||||
if !ok {
|
||||
return Err(Error::invalid_value(val_str,
|
||||
p_vals,
|
||||
arg,
|
||||
|
|
|
@ -2390,6 +2390,59 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
self
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
///
|
||||
/// **Pro Tip:** Use this setting with [`arg_enum!`]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// # use std::ascii::AsciiExt;
|
||||
/// let m = App::new("pv")
|
||||
/// .arg(Arg::with_name("option")
|
||||
/// .long("--option")
|
||||
/// .takes_value(true)
|
||||
/// .possible_value("test123")
|
||||
/// .case_insensitive(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "pv", "--option", "TeSt123",
|
||||
/// ]);
|
||||
///
|
||||
/// assert!(m.value_of("option").unwrap().eq_ignore_ascii_case("test123"));
|
||||
/// ```
|
||||
///
|
||||
/// This setting also works when multiple values can be defined:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("pv")
|
||||
/// .arg(Arg::with_name("option")
|
||||
/// .short("-o")
|
||||
/// .long("--option")
|
||||
/// .takes_value(true)
|
||||
/// .possible_value("test123")
|
||||
/// .possible_value("test321")
|
||||
/// .multiple(true)
|
||||
/// .case_insensitive(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "pv", "--option", "TeSt123", "teST123", "tESt321"
|
||||
/// ]);
|
||||
///
|
||||
/// let matched_vals = m.values_of("option").unwrap().collect::<Vec<_>>();
|
||||
/// assert_eq!(&*matched_vals, &["TeSt123", "teST123", "tESt321"]);
|
||||
/// ```
|
||||
/// [`Arg::case_insensitive(true)`]: ./struct.Arg.html#method.possible_values
|
||||
/// [`arg_enum!`]: ./macro.arg_enum.html
|
||||
pub fn case_insensitive(self, ci: bool) -> Self {
|
||||
if ci {
|
||||
self.set(ArgSettings::CaseInsensitive)
|
||||
} else {
|
||||
self.unset(ArgSettings::CaseInsensitive)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the name of the [`ArgGroup`] the argument belongs to.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::ascii::AsciiExt;
|
|||
use std::str::FromStr;
|
||||
|
||||
bitflags! {
|
||||
struct Flags: u16 {
|
||||
struct Flags: u32 {
|
||||
const REQUIRED = 1;
|
||||
const MULTIPLE = 1 << 1;
|
||||
const EMPTY_VALS = 1 << 2;
|
||||
|
@ -21,6 +21,7 @@ bitflags! {
|
|||
const REQUIRE_EQUALS = 1 << 13;
|
||||
const LAST = 1 << 14;
|
||||
const HIDE_DEFAULT_VAL = 1 << 15;
|
||||
const CASE_INSENSITIVE = 1 << 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +48,7 @@ impl ArgFlags {
|
|||
AllowLeadingHyphen => Flags::ALLOW_TAC_VALS,
|
||||
RequireEquals => Flags::REQUIRE_EQUALS,
|
||||
Last => Flags::LAST,
|
||||
CaseInsensitive => Flags::CASE_INSENSITIVE,
|
||||
HideDefaultValue => Flags::HIDE_DEFAULT_VAL
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +94,8 @@ pub enum ArgSettings {
|
|||
Last,
|
||||
/// Hides the default value from the help string
|
||||
HideDefaultValue,
|
||||
/// Makes `Arg::possible_values` case insensitive
|
||||
CaseInsensitive,
|
||||
#[doc(hidden)]
|
||||
RequiredUnlessAll,
|
||||
#[doc(hidden)]
|
||||
|
@ -118,6 +122,7 @@ impl FromStr for ArgSettings {
|
|||
"requireequals" => Ok(ArgSettings::RequireEquals),
|
||||
"last" => Ok(ArgSettings::Last),
|
||||
"hidedefaultvalue" => Ok(ArgSettings::HideDefaultValue),
|
||||
"caseinsensitive" => Ok(ArgSettings::CaseInsensitive),
|
||||
_ => Err("unknown ArgSetting, cannot convert from str".to_owned()),
|
||||
}
|
||||
}
|
||||
|
@ -161,6 +166,8 @@ mod test {
|
|||
ArgSettings::Last);
|
||||
assert_eq!("hidedefaultvalue".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::HideDefaultValue);
|
||||
assert_eq!("caseinsensitive".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::CaseInsensitive);
|
||||
assert!("hahahaha".parse::<ArgSettings>().is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,7 +262,8 @@ macro_rules! _clap_count_exprs {
|
|||
/// retrieve a `Vec<&'static str>` of the variant names, as well as implementing [`FromStr`] and
|
||||
/// [`Display`] automatically.
|
||||
///
|
||||
/// **NOTE:** Case insensitivity is supported for ASCII characters only
|
||||
/// **NOTE:** Case insensitivity is supported for ASCII characters only. It's highly recommended to
|
||||
/// use [`Arg::case_insensitive(true)`] for args that will be used with these enums
|
||||
///
|
||||
/// **NOTE:** This macro automatically implements [`std::str::FromStr`] and [`std::fmt::Display`]
|
||||
///
|
||||
|
@ -270,12 +271,12 @@ macro_rules! _clap_count_exprs {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate clap;
|
||||
/// # use clap::{App, Arg};
|
||||
/// arg_enum!{
|
||||
/// #[derive(Debug)]
|
||||
/// #[derive(PartialEq, Debug)]
|
||||
/// pub enum Foo {
|
||||
/// Bar,
|
||||
/// Baz,
|
||||
|
@ -286,17 +287,22 @@ macro_rules! _clap_count_exprs {
|
|||
/// // and implements std::str::FromStr to use with the value_t! macros
|
||||
/// fn main() {
|
||||
/// let m = App::new("app")
|
||||
/// .arg_from_usage("<foo> 'the foo'")
|
||||
/// .get_matches();
|
||||
/// .arg(Arg::from_usage("<foo> 'the foo'")
|
||||
/// .possible_values(&Foo::variants())
|
||||
/// .case_insensitive(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "app", "baz"
|
||||
/// ]);
|
||||
/// let f = value_t!(m, "foo", Foo).unwrap_or_else(|e| e.exit());
|
||||
///
|
||||
/// // Use f like any other Foo variant...
|
||||
/// assert_eq!(f, Foo::Baz);
|
||||
/// }
|
||||
/// ```
|
||||
/// [`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
|
||||
/// [`std::str::FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
|
||||
/// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||
/// [`std::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||
/// [`Arg::case_insensitive(true)`]: ./struct.Arg.html#method.case_insensitive
|
||||
#[macro_export]
|
||||
macro_rules! arg_enum {
|
||||
(@as_item $($i:item)*) => ($($i)*);
|
||||
|
|
|
@ -3,6 +3,9 @@ extern crate regex;
|
|||
|
||||
include!("../clap-test.rs");
|
||||
|
||||
#[allow(unsused_imports)]
|
||||
use std::ascii::AsciiExt;
|
||||
|
||||
use clap::{App, Arg, ErrorKind};
|
||||
|
||||
#[cfg(feature="suggestions")]
|
||||
|
|
Loading…
Reference in a new issue