mirror of
https://github.com/clap-rs/clap
synced 2024-12-16 15:52:44 +00:00
Merge pull request #9 from kbknapp/arg_enum_case_sensitive
Add case sensitive option for ArgEnum
This commit is contained in:
commit
f94ef37893
6 changed files with 128 additions and 3 deletions
28
examples/arg_enum_case_sensitive.rs
Normal file
28
examples/arg_enum_case_sensitive.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap_derive;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
#[derive(ArgEnum, Debug)]
|
||||||
|
#[case_sensitive]
|
||||||
|
enum ArgChoice {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||||
|
.arg(Arg::with_name("arg")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&ArgChoice::variants())
|
||||||
|
).get_matches();
|
||||||
|
|
||||||
|
let t = value_t!(matches.value_of("arg"), ArgChoice)
|
||||||
|
.unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
|
println!("{:?}", t);
|
||||||
|
}
|
|
@ -20,25 +20,32 @@ impl ClapDerive for ArgEnum {
|
||||||
|
|
||||||
fn impl_from_str(ast: &DeriveInput) -> Result<Tokens> {
|
fn impl_from_str(ast: &DeriveInput) -> Result<Tokens> {
|
||||||
let ident = &ast.ident;
|
let ident = &ast.ident;
|
||||||
|
let is_case_sensitive = ast.attrs.iter().any(|v| v.name() == "case_sensitive");
|
||||||
let variants = helpers::variants(ast)?;
|
let variants = helpers::variants(ast)?;
|
||||||
|
|
||||||
let strings = variants.iter()
|
let strings = variants.iter()
|
||||||
.map(|ref variant| String::from(variant.ident.as_ref()))
|
.map(|ref variant| String::from(variant.ident.as_ref()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Yes, we actually need to do this.
|
// All of these need to be iterators.
|
||||||
let ident_slice = [ident.clone()];
|
let ident_slice = [ident.clone()];
|
||||||
let idents = ident_slice.iter().cycle();
|
let idents = ident_slice.iter().cycle();
|
||||||
|
|
||||||
let for_error_message = strings.clone();
|
let for_error_message = strings.clone();
|
||||||
|
|
||||||
|
let condition_function_slice = [match is_case_sensitive {
|
||||||
|
true => quote! { str::eq },
|
||||||
|
false => quote! { ::std::ascii::AsciiExt::eq_ignore_ascii_case },
|
||||||
|
}];
|
||||||
|
let condition_function = condition_function_slice.iter().cycle();
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl ::std::str::FromStr for #ident {
|
impl ::std::str::FromStr for #ident {
|
||||||
type Err = String;
|
type Err = String;
|
||||||
|
|
||||||
fn from_str(input: &str) -> ::std::result::Result<Self, Self::Err> {
|
fn from_str(input: &str) -> ::std::result::Result<Self, Self::Err> {
|
||||||
match input {
|
match input {
|
||||||
#(val if ::std::ascii::AsciiExt::eq_ignore_ascii_case(val, #strings) => Ok(#idents::#variants),)*
|
#(val if #condition_function(val, #strings) => Ok(#idents::#variants),)*
|
||||||
_ => Err({
|
_ => Err({
|
||||||
let v = #for_error_message;
|
let v = #for_error_message;
|
||||||
format!("valid values: {}",
|
format!("valid values: {}",
|
||||||
|
|
|
@ -44,7 +44,7 @@ trait ClapDerive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// It is required to have this seperate and specificly defined.
|
/// It is required to have this seperate and specificly defined.
|
||||||
#[proc_macro_derive(ArgEnum)]
|
#[proc_macro_derive(ArgEnum, attributes(case_sensitive))]
|
||||||
pub fn derive_arg_enum(input: TokenStream) -> TokenStream {
|
pub fn derive_arg_enum(input: TokenStream) -> TokenStream {
|
||||||
ArgEnum::derive(input).unwrap()
|
ArgEnum::derive(input).unwrap()
|
||||||
}
|
}
|
45
tests/arg_enum_basic.rs
Normal file
45
tests/arg_enum_basic.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap_derive;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
#[derive(ArgEnum, Debug, PartialEq)]
|
||||||
|
enum ArgChoice {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_lowercase() {
|
||||||
|
let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||||
|
.arg(Arg::with_name("arg")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&ArgChoice::variants())
|
||||||
|
).get_matches_from_safe(vec![
|
||||||
|
"",
|
||||||
|
"foo",
|
||||||
|
]).unwrap();
|
||||||
|
let t = value_t!(matches.value_of("arg"), ArgChoice);
|
||||||
|
assert!(t.is_ok());
|
||||||
|
assert_eq!(t.unwrap(), ArgChoice::Foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_capitalized() {
|
||||||
|
let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||||
|
.arg(Arg::with_name("arg")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&ArgChoice::variants())
|
||||||
|
).get_matches_from_safe(vec![
|
||||||
|
"",
|
||||||
|
"Foo",
|
||||||
|
]).unwrap();
|
||||||
|
let t = value_t!(matches.value_of("arg"), ArgChoice);
|
||||||
|
assert!(t.is_ok());
|
||||||
|
assert_eq!(t.unwrap(), ArgChoice::Foo);
|
||||||
|
}
|
45
tests/arg_enum_case_sensitive.rs
Normal file
45
tests/arg_enum_case_sensitive.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap_derive;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
#[derive(ArgEnum, Debug, PartialEq)]
|
||||||
|
#[case_sensitive]
|
||||||
|
enum ArgChoice {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_lowercase() {
|
||||||
|
let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||||
|
.arg(Arg::with_name("arg")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&ArgChoice::variants())
|
||||||
|
).get_matches_from_safe(vec![
|
||||||
|
"",
|
||||||
|
"foo",
|
||||||
|
]); // We expect this to fail.
|
||||||
|
assert!(matches.is_err());
|
||||||
|
assert_eq!(matches.unwrap_err().kind, clap::ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_capitalized() {
|
||||||
|
let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||||
|
.arg(Arg::with_name("arg")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&ArgChoice::variants())
|
||||||
|
).get_matches_from_safe(vec![
|
||||||
|
"",
|
||||||
|
"Foo",
|
||||||
|
]).unwrap();
|
||||||
|
let t = value_t!(matches.value_of("arg"), ArgChoice);
|
||||||
|
assert!(t.is_ok());
|
||||||
|
assert_eq!(t.unwrap(), ArgChoice::Foo);
|
||||||
|
}
|
Loading…
Reference in a new issue