Merge pull request #9 from kbknapp/arg_enum_case_sensitive

Add case sensitive option for ArgEnum
This commit is contained in:
Kevin K 2017-11-26 08:09:11 -05:00 committed by GitHub
commit f94ef37893
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 128 additions and 3 deletions

View 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);
}

View file

@ -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: {}",

View file

@ -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
View 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);
}

View 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);
}