Skip to content

Commit

Permalink
ArgEnum: Slice instead of array, from_str in ArgEnum implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
ModProg committed Sep 29, 2021
1 parent ac1a9d6 commit 480035a
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 42 deletions.
2 changes: 1 addition & 1 deletion clap_derive/examples/arg_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use clap::{ArgEnum, Clap};

#[derive(ArgEnum, Debug, PartialEq)]
#[derive(ArgEnum, Debug, PartialEq, Clone)]
enum ArgChoice {
/// Descriptions are supported as doc-comment
Foo,
Expand Down
2 changes: 1 addition & 1 deletion clap_derive/examples/value_hints_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::ffi::OsString;
use std::io;
use std::path::PathBuf;

#[derive(ArgEnum, Debug, PartialEq)]
#[derive(ArgEnum, Debug, PartialEq, Clone)]
enum GeneratorChoice {
Bash,
Elvish,
Expand Down
23 changes: 4 additions & 19 deletions clap_derive/src/derives/arg_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr

let lits = lits(&e.variants, &attrs);
let arg_values = gen_arg_values(&lits);
let from_str = gen_from_str(&lits);
let arg_value = gen_arg_value(&lits);

quote! {
Expand All @@ -66,7 +65,6 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr
#[deny(clippy::correctness)]
impl clap::ArgEnum for #name {
#arg_values
#from_str
#arg_value
}
}
Expand Down Expand Up @@ -102,24 +100,11 @@ fn lits(
}

fn gen_arg_values(lits: &[(TokenStream, Ident)]) -> TokenStream {
let lit = lits.iter().map(|l| &l.0).collect::<Vec<_>>();
let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();

quote! {
fn arg_values() -> Vec<clap::ArgValue<'static>> {
vec![#(#lit),*]
}
}
}

fn gen_from_str(lits: &[(TokenStream, Ident)]) -> TokenStream {
let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();

quote! {
fn from_str(input: &str, case_insensitive: bool) -> ::std::result::Result<Self, String> {
match input {
#(val if #lit.matches(val, case_insensitive) => Ok(Self::#variant),)*
e => Err(format!("Invalid variant: {}", e)),
}
fn value_variants<'a>() -> &'a [Self]{
&[#(Self::#lit),*]
}
}
}
Expand All @@ -128,7 +113,7 @@ fn gen_arg_value(lits: &[(TokenStream, Ident)]) -> TokenStream {
let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();

quote! {
fn arg_value(&self) -> Option<clap::ArgValue<'static>> {
fn arg_value<'a>(&self) -> Option<clap::ArgValue<'a>> {
match self {
#(Self::#variant => Some(#lit),)*
_ => None
Expand Down
2 changes: 1 addition & 1 deletion clap_derive/src/derives/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ pub fn gen_augment(

fn gen_arg_enum_possible_values(ty: &Type) -> TokenStream {
quote_spanned! { ty.span()=>
.possible_values(<#ty as clap::ArgEnum>::arg_values())
.possible_values(<#ty as clap::ArgEnum>::value_variants().iter().filter_map(clap::ArgEnum::arg_value))
}
}

Expand Down
34 changes: 19 additions & 15 deletions clap_derive/tests/arg_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use clap::{ArgEnum, ArgValue, Clap};

#[test]
fn basic() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
Expand Down Expand Up @@ -40,7 +40,7 @@ fn basic() {

#[test]
fn default_value() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
Expand Down Expand Up @@ -86,7 +86,7 @@ fn default_value() {

#[test]
fn multi_word_is_renamed_kebab() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
#[allow(non_camel_case_types)]
enum ArgChoice {
FooBar,
Expand Down Expand Up @@ -116,7 +116,7 @@ fn multi_word_is_renamed_kebab() {

#[test]
fn variant_with_defined_casing() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
#[clap(rename_all = "screaming_snake")]
FooBar,
Expand All @@ -139,7 +139,7 @@ fn variant_with_defined_casing() {

#[test]
fn casing_is_propogated_from_parent() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
#[clap(rename_all = "screaming_snake")]
enum ArgChoice {
FooBar,
Expand All @@ -162,7 +162,7 @@ fn casing_is_propogated_from_parent() {

#[test]
fn casing_propogation_is_overridden() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
#[clap(rename_all = "screaming_snake")]
enum ArgChoice {
#[clap(rename_all = "camel")]
Expand All @@ -187,7 +187,7 @@ fn casing_propogation_is_overridden() {

#[test]
fn case_insensitive() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
Expand All @@ -214,7 +214,7 @@ fn case_insensitive() {

#[test]
fn case_insensitive_set_to_false() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
Expand All @@ -236,7 +236,7 @@ fn case_insensitive_set_to_false() {

#[test]
fn alias() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
#[clap(alias = "TOTP")]
TOTP,
Expand Down Expand Up @@ -264,7 +264,7 @@ fn alias() {

#[test]
fn multiple_alias() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
#[clap(alias = "TOTP", alias = "t")]
TOTP,
Expand Down Expand Up @@ -298,7 +298,7 @@ fn multiple_alias() {

#[test]
fn option() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
Expand Down Expand Up @@ -328,7 +328,7 @@ fn option() {

#[test]
fn vector() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
Expand Down Expand Up @@ -358,7 +358,7 @@ fn vector() {

#[test]
fn skip_variant() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
#[allow(dead_code)] // silence warning about `Baz` being unused
enum ArgChoice {
Foo,
Expand All @@ -368,7 +368,11 @@ fn skip_variant() {
}

assert_eq!(
ArgChoice::arg_values(),
ArgChoice::value_variants()
.iter()
.map(ArgEnum::arg_value)
.map(Option::unwrap)
.collect::<Vec<_>>(),
vec![ArgValue::new("foo"), ArgValue::new("bar")]
);
assert!(ArgChoice::from_str("foo", true).is_ok());
Expand All @@ -378,7 +382,7 @@ fn skip_variant() {

#[test]
fn from_str_invalid() {
#[derive(ArgEnum, PartialEq, Debug)]
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
Expand Down
16 changes: 11 additions & 5 deletions src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,25 +273,31 @@ pub trait Subcommand: FromArgMatches + Sized {
/// level: Level,
/// }
///
/// #[derive(clap::ArgEnum)]
/// #[derive(clap::ArgEnum, Clone)]
/// enum Level {
/// Debug,
/// Info,
/// Warning,
/// Error,
/// }
/// ```
pub trait ArgEnum: Sized {
pub trait ArgEnum: Sized + Clone {
/// All possible argument values, in display order.
fn arg_values() -> Vec<ArgValue<'static>>;
fn value_variants<'a>() -> &'a [Self];

/// Parse an argument into `Self`.
fn from_str(input: &str, case_insensitive: bool) -> Result<Self, String>;
fn from_str(input: &str, case_insensitive: bool) -> Result<Self, String> {
Self::value_variants()
.iter()
.find(|v| v.arg_value().unwrap().matches(input, case_insensitive))
.cloned()
.ok_or_else(|| format!("Invalid variant: {}", input))
}

/// The canonical argument value.
///
/// The value is `None` for skipped variants.
fn arg_value(&self) -> Option<ArgValue<'static>>;
fn arg_value<'a>(&self) -> Option<ArgValue<'a>>;
}

impl<T: Clap> Clap for Box<T> {
Expand Down

0 comments on commit 480035a

Please sign in to comment.