From d1fc83a8fa22c39d1fbcc85399ed6dab1c1601c0 Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Wed, 29 Apr 2020 11:44:38 +0200 Subject: [PATCH 01/11] wip: Add update_from_arg_matches to FromArgMatches --- clap_derive/src/derives/from_arg_matches.rs | 206 ++++++++++++++++++++ clap_derive/src/derives/subcommand.rs | 182 +++++++++++++++++ clap_derive/src/dummies.rs | 6 + src/derive.rs | 11 ++ 4 files changed, 405 insertions(+) diff --git a/clap_derive/src/derives/from_arg_matches.rs b/clap_derive/src/derives/from_arg_matches.rs index d6348ca4..32c1c9fc 100644 --- a/clap_derive/src/derives/from_arg_matches.rs +++ b/clap_derive/src/derives/from_arg_matches.rs @@ -27,6 +27,7 @@ pub fn gen_for_struct( parent_attribute: &Attrs, ) -> TokenStream { let constructor = gen_constructor(fields, parent_attribute); + let updater = gen_updater(fields, parent_attribute, true); quote! { #[allow(dead_code, unreachable_code, unused_variables)] @@ -45,6 +46,10 @@ pub fn gen_for_struct( fn from_arg_matches(matches: &::clap::ArgMatches) -> Self { #struct_name #constructor } + + fn update_from_arg_matches(&mut self, matches: &::clap::ArgMatches) { + #updater + } } } } @@ -67,6 +72,10 @@ pub fn gen_for_enum(name: &Ident) -> TokenStream { fn from_arg_matches(matches: &::clap::ArgMatches) -> Self { <#name as ::clap::Subcommand>::from_subcommand(matches.subcommand()).unwrap() } + fn update_from_arg_matches(&mut self, matches: &::clap::ArgMatches) { + let (name, subcmd) = matches.subcommand(); + <#name as ::clap::Subcommand>::update_from_subcommand(self, name, subcmd); + } } } } @@ -239,3 +248,200 @@ pub fn gen_constructor(fields: &Punctuated, parent_attribute: &Att #( #fields ),* }} } + +pub fn gen_updater( + fields: &Punctuated, + parent_attribute: &Attrs, + use_self: bool, +) -> TokenStream { + let fields = fields.iter().map(|field| { + let attrs = Attrs::from_field( + field, + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + let field_name = field.ident.as_ref().unwrap(); + let kind = attrs.kind(); + + let access = if use_self { + quote! { + #[allow(non_snake_case)] + let #field_name = &mut self.#field_name; + } + } else { + quote!() + }; + + match &*kind { + Kind::ExternalSubcommand => { + abort! { kind.span(), + "`external_subcommand` can be used only on enum variants" + } + } + Kind::Subcommand(ty) => { + let subcmd_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + + let updater = quote_spanned!{ ty.span()=> + <#subcmd_type as ::clap::Subcommand>::update_from_subcommand(#field_name, name, subcmd); + }; + + let updater = match **ty { + Ty::Option => quote_spanned! { kind.span()=> + if let Some(#field_name) = #field_name.as_mut() { + #updater + } else { + *#field_name = <#subcmd_type as ::clap::Subcommand>::from_subcommand( + name, + subcmd + ) + } + }, + _ => quote_spanned!{ kind.span()=> + #updater + } + }; + + quote_spanned! { kind.span()=> + { + let (name, subcmd) = matches.subcommand(); + #access + #updater + } + } + } + + Kind::Flatten => quote_spanned! { kind.span()=> { + #access + ::clap::FromArgMatches::update_from_arg_matches(#field_name, matches); + } + }, + + Kind::Skip(_) => quote!(), + + Kind::Arg(ty) => { + use self::ParserKind::*; + + let parser = attrs.parser(); + let func = &parser.func; + let span = parser.kind.span(); + let (value_of, values_of, mut parse) = match *parser.kind { + FromStr => ( + quote_spanned!(span=> value_of), + quote_spanned!(span=> values_of), + func.clone(), + ), + TryFromStr => ( + quote_spanned!(span=> value_of), + quote_spanned!(span=> values_of), + quote_spanned!(func.span()=> |s| #func(s).unwrap()), + ), + FromOsStr => ( + quote_spanned!(span=> value_of_os), + quote_spanned!(span=> values_of_os), + func.clone(), + ), + TryFromOsStr => ( + quote_spanned!(span=> value_of_os), + quote_spanned!(span=> values_of_os), + quote_spanned!(func.span()=> |s| #func(s).unwrap()), + ), + FromOccurrences => ( + quote_spanned!(span=> occurrences_of), + quote!(), + func.clone(), + ), + FromFlag => (quote!(), quote!(), func.clone()), + }; + + let flag = *attrs.parser().kind == ParserKind::FromFlag; + let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences; + let name = attrs.cased_name(); + + let field_value = match **ty { + Ty::Bool => quote_spanned! { ty.span()=> + *#field_name || matches.is_present(#name) + }, + + Ty::Option => { + if attrs.is_enum() { + if let Some(subty) = sub_type(&field.ty) { + parse = gen_arg_enum_parse(subty, &attrs); + } + } + + quote_spanned! { ty.span()=> + matches.#value_of(#name) + .map(#parse) + } + } + + Ty::OptionOption => quote_spanned! { ty.span()=> + if matches.is_present(#name) { + Some(matches.#value_of(#name).map(#parse)) + } else { + None + } + }, + + Ty::OptionVec => quote_spanned! { ty.span()=> + if matches.is_present(#name) { + Some(matches.#values_of(#name) + .map(|v| v.map(#parse).collect()) + .unwrap_or_else(Vec::new)) + } else { + None + } + }, + + Ty::Vec => { + if attrs.is_enum() { + if let Some(subty) = subty_if_name(&field.ty, "Vec") { + parse = gen_arg_enum_parse(subty, &attrs); + } + } + + quote_spanned! { ty.span()=> + matches.#values_of(#name) + .map(|v| v.map(#parse).collect()) + .unwrap_or_else(Vec::new) + } + } + + Ty::Other if occurrences => quote_spanned! { ty.span()=> + #parse(matches.#value_of(#name)) + }, + + Ty::Other if flag => quote_spanned! { ty.span()=> + #parse(matches.is_present(#name)) + }, + + Ty::Other => { + if attrs.is_enum() { + parse = gen_arg_enum_parse(&field.ty, &attrs); + } + + quote_spanned! { ty.span()=> + matches.#value_of(#name) + .map(#parse) + .unwrap() + } + } + }; + + quote_spanned! { field.span()=> + if matches.is_present(#name) { + #access + *#field_name = #field_value + } + } + } + } + }); + + quote! { + #( #fields )* + } +} diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index 1977c9d1..b196078f 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -35,6 +35,7 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr let from_subcommand = gen_from_subcommand(name, &e.variants, &attrs); let augment_subcommands = gen_augment_subcommands(&e.variants, &attrs); + let update_from_subcommand = gen_update_from_subcommand(name, &e.variants, &attrs); quote! { #[allow(dead_code, unreachable_code, unused_variables)] @@ -52,6 +53,7 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr impl ::clap::Subcommand for #name { #augment_subcommands #from_subcommand + #update_from_subcommand } } } @@ -291,3 +293,183 @@ fn gen_from_subcommand( } } } + +fn gen_update_from_subcommand( + name: &Ident, + variants: &Punctuated, + parent_attribute: &Attrs, +) -> TokenStream { + use syn::Fields::*; + + let mut ext_subcmd = None; + let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants + .iter() + .filter_map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + + if let Kind::ExternalSubcommand = &*attrs.kind() { + if ext_subcmd.is_some() { + abort!( + attrs.kind().span(), + "Only one variant can be marked with `external_subcommand`, \ + this is the second" + ); + } + + let ty = match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, + + _ => abort!( + variant, + "The enum variant marked with `external_attribute` must be \ + a single-typed tuple, and the type must be either `Vec` \ + or `Vec`." + ), + }; + + let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") { + Some(subty) => { + if is_simple_ty(subty, "String") { + ( + subty.span(), + quote!(::std::string::String), + quote!(values_of), + ) + } else if is_simple_ty(subty, "OsString") { + ( + subty.span(), + quote!(::std::ffi::OsString), + quote!(values_of_os), + ) + } else { + abort!( + ty.span(), + "The type must be either `Vec` or `Vec` \ + to be used with `external_subcommand`." + ) + } + } + + None => abort!( + ty.span(), + "The type must be either `Vec` or `Vec` \ + to be used with `external_subcommand`." + ), + }; + + ext_subcmd = Some((span, &variant.ident, str_ty, values_of)); + None + } else { + Some((variant, attrs)) + } + }) + .partition(|(_, attrs)| { + let kind = attrs.kind(); + match &*kind { + Kind::Flatten => true, + _ => false, + } + }); + + let subcommands = variants.iter().map(|(variant, attrs)| { + let sub_name = attrs.cased_name(); + let variant_name = &variant.ident; + let (pattern, updater) = match variant.fields { + Named(ref fields) => { + let (fields, update): (Vec<_>, Vec<_>) = fields + .named + .iter() + .map(|field| { + let attrs = Attrs::from_field( + field, + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + let field_name = field.ident.as_ref().unwrap(); + ( + quote!( ref mut #field_name ), + from_arg_matches::gen_updater(&fields.named, &attrs, false), + ) + }) + .unzip(); + (quote!( { #( #fields, )* }), quote!( { #( #update )* } )) + } + Unit => (quote!(), quote!({})), + Unnamed(ref fields) => { + if fields.unnamed.len() == 1 { + ( + quote!((ref mut arg)), + quote!(::clap::FromArgMatches::update_from_arg_matches( + arg, matches + )), + ) + } else { + abort_call_site!("{}: tuple enums are not supported", variant.ident) + } + } + }; + + quote! { + (#sub_name, #name :: #variant_name #pattern) => { #updater } + } + }); + + let child_subcommands = flatten_variants.iter().map(|(variant, attrs)| { + let sub_name = attrs.cased_name(); + let variant_name = &variant.ident; + let (pattern, updater) = match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => ( + quote!((ref mut arg)), + quote! { + println!("child"); + }, + ), + _ => abort!( + variant, + "`flatten` is usable only with single-typed tuple variants" + ), + }; + quote! { + (#sub_name, #name :: #variant_name #pattern) => { #updater } + } + }); + + let external = if let Some((span, variant_name, str_ty, values_of)) = ext_subcmd { + quote_spanned! { span=> + (external, #name :: #variant_name (ref mut ext)) => { + if ext.len() == 0 { + ext.push(#str_ty::from(external)); + ext.extend(matches.#values_of("").into_iter().flatten().map(#str_ty::from)); + } + } + } + } else { + quote!() + }; + + quote! { + fn update_from_subcommand<'b>( + &mut self, + name: &'b str, + sub: Option<&'b ::clap::ArgMatches>) + { + if let Some(matches) = sub { + match (name, self) { + #( #subcommands ),* + #( #child_subcommands ),* + #external + (_, s) => if let Some(sub) = ::from_subcommand(name, sub) { + *s = sub; + } + + } + } + } + } +} diff --git a/clap_derive/src/dummies.rs b/clap_derive/src/dummies.rs index 4d8d2af3..4cc70aee 100644 --- a/clap_derive/src/dummies.rs +++ b/clap_derive/src/dummies.rs @@ -37,6 +37,9 @@ pub fn from_arg_matches(name: &Ident) { fn from_arg_matches(_m: &::clap::ArgMatches) -> Self { unimplemented!() } + fn update_from_arg_matches(&mut self, matches: &::clap::ArgMatches) { + unimplemented!() + } } }); } @@ -47,6 +50,9 @@ pub fn subcommand(name: &Ident) { fn from_subcommand(_sub: Option<(&str, &::clap::ArgMatches)>) -> Option { unimplemented!() } + fn update_from_subcommand(&mut self, _name: &str, _matches: Option<&::clap::ArgMatches>) { + unimplemented!() + } fn augment_subcommands(_app: ::clap::App<'_>) -> ::clap::App<'_> { unimplemented!() } diff --git a/src/derive.rs b/src/derive.rs index b91eed2f..f5124d43 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -154,6 +154,9 @@ pub trait FromArgMatches: Sized { /// } /// ``` fn from_arg_matches(matches: &ArgMatches) -> Self; + + /// @TODO@ @release @docs + fn update_from_arg_matches(&mut self, matches: &ArgMatches); } /// @TODO @release @docs @@ -161,6 +164,8 @@ pub trait Subcommand: Sized { /// @TODO @release @docs fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option; /// @TODO @release @docs + fn update_from_subcommand(&mut self, name: &str, matches: Option<&ArgMatches>); + /// @TODO @release @docs fn augment_subcommands(app: App<'_>) -> App<'_>; } @@ -214,12 +219,18 @@ impl FromArgMatches for Box { fn from_arg_matches(matches: &ArgMatches) -> Self { Box::new(::from_arg_matches(matches)) } + fn update_from_arg_matches(&mut self, matches: &ArgMatches) { + ::update_from_arg_matches(self, matches); + } } impl Subcommand for Box { fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option { ::from_subcommand(subcommand).map(Box::new) } + fn update_from_subcommand(&mut self, name: &str, matches: Option<&ArgMatches>) { + ::update_from_subcommand(self, name, matches); + } fn augment_subcommands(app: App<'_>) -> App<'_> { ::augment_subcommands(app) } From 646b3fe8113d2e311f71b8387108ebf83420be82 Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Tue, 7 Jul 2020 13:17:36 +0200 Subject: [PATCH 02/11] wip: Add tests and user-facing functions --- clap_derive/tests/basic.rs | 15 ++++++ clap_derive/tests/boxed.rs | 54 ++++++++++++++-------- clap_derive/tests/custom-string-parsers.rs | 22 +++++++++ clap_derive/tests/flatten.rs | 53 ++++++++++++--------- src/derive.rs | 26 +++++++++++ 5 files changed, 129 insertions(+), 41 deletions(-) diff --git a/clap_derive/tests/basic.rs b/clap_derive/tests/basic.rs index ac7cede7..57745377 100644 --- a/clap_derive/tests/basic.rs +++ b/clap_derive/tests/basic.rs @@ -29,6 +29,21 @@ fn basic() { ); } +#[test] +fn update_basic() { + #[derive(Clap, PartialEq, Debug)] + struct Opt { + #[clap(short = 'a', long = "arg")] + single_value: i32, + } + + let mut opt = Opt::parse_from(&["test", "-a0"]); + + opt.update_from(&["test", "-a42"]); + + assert_eq!(Opt { single_value: 42 }, opt); +} + #[test] fn unit_struct() { #[derive(Clap, PartialEq, Debug)] diff --git a/clap_derive/tests/boxed.rs b/clap_derive/tests/boxed.rs index 5ba62244..27290e0b 100644 --- a/clap_derive/tests/boxed.rs +++ b/clap_derive/tests/boxed.rs @@ -1,26 +1,26 @@ use clap::Clap; +#[derive(Clap, PartialEq, Debug)] +struct Opt { + #[clap(subcommand)] + sub: Box, +} + +#[derive(Clap, PartialEq, Debug)] +enum Sub { + Flame { + #[clap(flatten)] + arg: Box, + }, +} + +#[derive(Clap, PartialEq, Debug)] +struct Ext { + arg: u32, +} + #[test] fn boxed_flatten_subcommand() { - #[derive(Clap, PartialEq, Debug)] - struct Opt { - #[clap(subcommand)] - sub: Box, - } - - #[derive(Clap, PartialEq, Debug)] - enum Sub { - Flame { - #[clap(flatten)] - arg: Box, - }, - } - - #[derive(Clap, PartialEq, Debug)] - struct Ext { - arg: u32, - } - assert_eq!( Opt { sub: Box::new(Sub::Flame { @@ -30,3 +30,19 @@ fn boxed_flatten_subcommand() { Opt::parse_from(&["test", "flame", "1"]) ); } + +#[test] +fn update_boxed_flatten_subcommand() { + let mut opt = Opt::parse_from(&["test", "flame", "1"]); + + opt.update_from(&["test", "flame", "42"]); + + assert_eq!( + Opt { + sub: Box::new(Sub::Flame { + arg: Box::new(Ext { arg: 42 }) + }) + }, + opt + ); +} diff --git a/clap_derive/tests/custom-string-parsers.rs b/clap_derive/tests/custom-string-parsers.rs index 8c6f8ad0..ed93013b 100644 --- a/clap_derive/tests/custom-string-parsers.rs +++ b/clap_derive/tests/custom-string-parsers.rs @@ -133,6 +133,28 @@ fn test_every_custom_parser() { ); } +#[test] +fn update_every_custom_parser() { + let mut opt = NoOpOpt { + a: "0", + b: "0", + c: "0", + d: "D", + }; + + opt.update_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"]); + + assert_eq!( + NoOpOpt { + a: "A", + b: "B", + c: "C", + d: "D" + }, + opt + ); +} + // Note: can't use `Vec` directly, as clap would instead look for // conversion function from `&str` to `u8`. type Bytes = Vec; diff --git a/clap_derive/tests/flatten.rs b/clap_derive/tests/flatten.rs index 702f1ae9..27419041 100644 --- a/clap_derive/tests/flatten.rs +++ b/clap_derive/tests/flatten.rs @@ -99,30 +99,30 @@ fn flatten_in_subcommand() { ); } +#[derive(Clap, PartialEq, Debug)] +enum BaseCli { + Command1(Command1), +} + +#[derive(Clap, PartialEq, Debug)] +struct Command1 { + arg1: i32, +} + +#[derive(Clap, PartialEq, Debug)] +struct Command2 { + arg2: i32, +} + +#[derive(Clap, PartialEq, Debug)] +enum Opt { + #[clap(flatten)] + BaseCli(BaseCli), + Command2(Command2), +} + #[test] fn merge_subcommands_with_flatten() { - #[derive(Clap, PartialEq, Debug)] - enum BaseCli { - Command1(Command1), - } - - #[derive(Clap, PartialEq, Debug)] - struct Command1 { - arg1: i32, - } - - #[derive(Clap, PartialEq, Debug)] - struct Command2 { - arg2: i32, - } - - #[derive(Clap, PartialEq, Debug)] - enum Opt { - #[clap(flatten)] - BaseCli(BaseCli), - Command2(Command2), - } - assert_eq!( Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 42 })), Opt::parse_from(&["test", "command1", "42"]) @@ -151,3 +151,12 @@ fn flatten_with_doc_comment() { opts: DaemonOpts, } } + +#[test] +fn update_subcommands_with_flatten() { + let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12 })); + opt.update_from(&["test", "command1", "42"]); + assert_eq!(Opt::parse_from(&["test", "command1", "42"]), opt); + opt.update_from(&["test", "command2", "43"]); + assert_eq!(Opt::parse_from(&["test", "command2", "43"]), opt); +} diff --git a/src/derive.rs b/src/derive.rs index f5124d43..6dd8a6dd 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -104,6 +104,32 @@ pub trait Clap: FromArgMatches + IntoApp + Sized { let matches = ::into_app().try_get_matches_from(itr)?; Ok(::from_arg_matches(&matches)) } + + /// Update from iterator, exit on error + fn update_from(&mut self, itr: I) + where + I: IntoIterator, + // TODO (@CreepySkeleton): discover a way to avoid cloning here + T: Into + Clone, + { + // TODO find a way to get partial matches + let matches = ::into_app().get_matches_from(itr); + ::update_from_arg_matches(self, &matches); + } + + /// Update from iterator, return Err on error. + fn try_update_from(&mut self, itr: I) -> Result<(), Error> + where + I: IntoIterator, + // TODO (@CreepySkeleton): discover a way to avoid cloning here + T: Into + Clone, + { + // TODO find a way to get partial matches + let matches = ::into_app().try_get_matches_from(itr)?; + Ok(::update_from_arg_matches( + self, &matches, + )) + } } /// Build an App according to the struct From 8b6255057d53bc1d28c268d2458bbaf4f19cecbb Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Tue, 7 Jul 2020 13:31:43 +0200 Subject: [PATCH 03/11] wip: Complete update_subcommand external subcommand updating is equivalent to replace it --- clap_derive/src/derives/subcommand.rs | 80 +++------------------------ src/derive.rs | 5 +- 2 files changed, 11 insertions(+), 74 deletions(-) diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index b196078f..b1401cd2 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -301,7 +301,6 @@ fn gen_update_from_subcommand( ) -> TokenStream { use syn::Fields::*; - let mut ext_subcmd = None; let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants .iter() .filter_map(|variant| { @@ -314,56 +313,6 @@ fn gen_update_from_subcommand( ); if let Kind::ExternalSubcommand = &*attrs.kind() { - if ext_subcmd.is_some() { - abort!( - attrs.kind().span(), - "Only one variant can be marked with `external_subcommand`, \ - this is the second" - ); - } - - let ty = match variant.fields { - Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, - - _ => abort!( - variant, - "The enum variant marked with `external_attribute` must be \ - a single-typed tuple, and the type must be either `Vec` \ - or `Vec`." - ), - }; - - let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") { - Some(subty) => { - if is_simple_ty(subty, "String") { - ( - subty.span(), - quote!(::std::string::String), - quote!(values_of), - ) - } else if is_simple_ty(subty, "OsString") { - ( - subty.span(), - quote!(::std::ffi::OsString), - quote!(values_of_os), - ) - } else { - abort!( - ty.span(), - "The type must be either `Vec` or `Vec` \ - to be used with `external_subcommand`." - ) - } - } - - None => abort!( - ty.span(), - "The type must be either `Vec` or `Vec` \ - to be used with `external_subcommand`." - ), - }; - - ext_subcmd = Some((span, &variant.ident, str_ty, values_of)); None } else { Some((variant, attrs)) @@ -424,12 +373,15 @@ fn gen_update_from_subcommand( let sub_name = attrs.cased_name(); let variant_name = &variant.ident; let (pattern, updater) = match variant.fields { - Unnamed(ref fields) if fields.unnamed.len() == 1 => ( - quote!((ref mut arg)), - quote! { - println!("child"); - }, - ), + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + ( + quote!((ref mut arg)), + quote! { + <#ty as ::clap::Subcommand>::update_from_subcommand(arg, name, sub); + }, + ) + } _ => abort!( variant, "`flatten` is usable only with single-typed tuple variants" @@ -440,19 +392,6 @@ fn gen_update_from_subcommand( } }); - let external = if let Some((span, variant_name, str_ty, values_of)) = ext_subcmd { - quote_spanned! { span=> - (external, #name :: #variant_name (ref mut ext)) => { - if ext.len() == 0 { - ext.push(#str_ty::from(external)); - ext.extend(matches.#values_of("").into_iter().flatten().map(#str_ty::from)); - } - } - } - } else { - quote!() - }; - quote! { fn update_from_subcommand<'b>( &mut self, @@ -463,7 +402,6 @@ fn gen_update_from_subcommand( match (name, self) { #( #subcommands ),* #( #child_subcommands ),* - #external (_, s) => if let Some(sub) = ::from_subcommand(name, sub) { *s = sub; } diff --git a/src/derive.rs b/src/derive.rs index 6dd8a6dd..e276a23f 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -126,9 +126,8 @@ pub trait Clap: FromArgMatches + IntoApp + Sized { { // TODO find a way to get partial matches let matches = ::into_app().try_get_matches_from(itr)?; - Ok(::update_from_arg_matches( - self, &matches, - )) + ::update_from_arg_matches(self, &matches); + Ok(()) } } From a9276576d70e5ae117d4d350e606e40c4cf6e55a Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Tue, 25 Aug 2020 11:03:09 +0200 Subject: [PATCH 04/11] wip: Add a variant for augment* for updates --- clap_derive/src/derives/into_app.rs | 33 +++++++++++++++++++++---- clap_derive/src/derives/subcommand.rs | 35 ++++++++++++++++++++++++--- clap_derive/src/dummies.rs | 9 +++++++ src/derive.rs | 20 ++++++++++++--- 4 files changed, 85 insertions(+), 12 deletions(-) diff --git a/clap_derive/src/derives/into_app.rs b/clap_derive/src/derives/into_app.rs index 01baca22..f879054a 100644 --- a/clap_derive/src/derives/into_app.rs +++ b/clap_derive/src/derives/into_app.rs @@ -103,6 +103,15 @@ pub fn gen_for_enum(name: &Ident) -> TokenStream { fn augment_clap<'b>(app: ::clap::App<'b>) -> ::clap::App<'b> { <#name as ::clap::Subcommand>::augment_subcommands(app) } + + fn into_update_app<'b>() -> ::clap::App<'b> { + let app = ::clap::App::new(#app_name); + <#name as ::clap::IntoApp>::augment_update_clap(app) + } + + fn augment_update_clap<'b>(app: ::clap::App<'b>) -> ::clap::App<'b> { + <#name as ::clap::Subcommand>::augment_update_subcommands(app) + } } } } @@ -123,6 +132,9 @@ fn gen_into_app_fn(attrs: &[Attribute]) -> GenOutput { fn into_app<'b>() -> ::clap::App<'b> { Self::augment_clap(::clap::App::new(#name)) } + fn into_update_app<'b>() -> ::clap::App<'b> { + Self::augment_update_clap(::clap::App::new(#name)) + } }; (tokens, attrs) @@ -130,11 +142,15 @@ fn gen_into_app_fn(attrs: &[Attribute]) -> GenOutput { fn gen_augment_clap_fn(fields: &Punctuated, parent_attribute: &Attrs) -> TokenStream { let app_var = Ident::new("app", Span::call_site()); - let augmentation = gen_app_augmentation(fields, &app_var, parent_attribute); + let augmentation = gen_app_augmentation(fields, &app_var, parent_attribute, false); + let augmentation_update = gen_app_augmentation(fields, &app_var, parent_attribute, true); quote! { fn augment_clap<'b>(#app_var: ::clap::App<'b>) -> ::clap::App<'b> { #augmentation } + fn augment_update_clap<'b>(#app_var: ::clap::App<'b>) -> ::clap::App<'b> { + #augmentation_update + } } } @@ -150,6 +166,7 @@ pub fn gen_app_augmentation( fields: &Punctuated, app_var: &Ident, parent_attribute: &Attrs, + override_required: bool, ) -> TokenStream { let mut subcmds = fields.iter().filter_map(|field| { let attrs = Attrs::from_field( @@ -174,9 +191,15 @@ pub fn gen_app_augmentation( }; let span = field.span(); - let ts = quote! { - let #app_var = <#subcmd_type as ::clap::Subcommand>::augment_subcommands( #app_var ); - #required + let ts = if override_required { + quote! { + let #app_var = <#subcmd_type as ::clap::Subcommand>::augment_update_subcommands( #app_var ); + } + } else{ + quote! { + let #app_var = <#subcmd_type as ::clap::Subcommand>::augment_subcommands( #app_var ); + #required + } }; Some((span, ts)) } else { @@ -296,7 +319,7 @@ pub fn gen_app_augmentation( }, Ty::Other => { - let required = !attrs.has_method("default_value"); + let required = !attrs.has_method("default_value") && !override_required; let mut possible_values = quote!(); if attrs.is_enum() { diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index b1401cd2..0c26472b 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -35,6 +35,7 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr let from_subcommand = gen_from_subcommand(name, &e.variants, &attrs); let augment_subcommands = gen_augment_subcommands(&e.variants, &attrs); + let augment_update_subcommands = gen_augment_update_subcommands(&e.variants, &attrs); let update_from_subcommand = gen_update_from_subcommand(name, &e.variants, &attrs); quote! { @@ -53,6 +54,7 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr impl ::clap::Subcommand for #name { #augment_subcommands #from_subcommand + #augment_update_subcommands #update_from_subcommand } } @@ -61,6 +63,27 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr fn gen_augment_subcommands( variants: &Punctuated, parent_attribute: &Attrs, +) -> TokenStream { + gen_augment("augment_subcommands", variants, parent_attribute, false) +} + +fn gen_augment_update_subcommands( + variants: &Punctuated, + parent_attribute: &Attrs, +) -> TokenStream { + gen_augment( + "augment_update_subcommands", + variants, + parent_attribute, + true, + ) +} + +fn gen_augment( + fn_name: &str, + variants: &Punctuated, + parent_attribute: &Attrs, + override_required: bool, ) -> TokenStream { use syn::Fields::*; @@ -99,9 +122,12 @@ fn gen_augment_subcommands( _ => { let app_var = Ident::new("subcommand", Span::call_site()); let arg_block = match variant.fields { - Named(ref fields) => { - into_app::gen_app_augmentation(&fields.named, &app_var, &attrs) - } + Named(ref fields) => into_app::gen_app_augmentation( + &fields.named, + &app_var, + &attrs, + override_required, + ), Unit => quote!( #app_var ), Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { let ty = &unnamed[0]; @@ -133,8 +159,9 @@ fn gen_augment_subcommands( let app_methods = parent_attribute.top_level_methods(); let version = parent_attribute.version(); + let fn_name = Ident::new(fn_name, Span::call_site()); quote! { - fn augment_subcommands<'b>(app: ::clap::App<'b>) -> ::clap::App<'b> { + fn #fn_name <'b>(app: ::clap::App<'b>) -> ::clap::App<'b> { let app = app #app_methods; #( #subcommands )*; app #version diff --git a/clap_derive/src/dummies.rs b/clap_derive/src/dummies.rs index 4cc70aee..e78c152d 100644 --- a/clap_derive/src/dummies.rs +++ b/clap_derive/src/dummies.rs @@ -27,6 +27,12 @@ pub fn into_app(name: &Ident) { fn augment_clap<'b>(_app: ::clap::App<'b>) -> ::clap::App<'b> { unimplemented!() } + fn into_update_app<'b>() -> ::clap::App<'b> { + unimplemented!() + } + fn augment_update_clap<'b>(_app: ::clap::App<'b>) -> ::clap::App<'b> { + unimplemented!() + } } }); } @@ -56,6 +62,9 @@ pub fn subcommand(name: &Ident) { fn augment_subcommands(_app: ::clap::App<'_>) -> ::clap::App<'_> { unimplemented!() } + fn augment_update_subcommands(_app: ::clap::App<'_>) -> ::clap::App<'_> { + unimplemented!() + } } }); } diff --git a/src/derive.rs b/src/derive.rs index e276a23f..b98b0823 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -113,7 +113,7 @@ pub trait Clap: FromArgMatches + IntoApp + Sized { T: Into + Clone, { // TODO find a way to get partial matches - let matches = ::into_app().get_matches_from(itr); + let matches = ::into_update_app().get_matches_from(itr); ::update_from_arg_matches(self, &matches); } @@ -124,8 +124,7 @@ pub trait Clap: FromArgMatches + IntoApp + Sized { // TODO (@CreepySkeleton): discover a way to avoid cloning here T: Into + Clone, { - // TODO find a way to get partial matches - let matches = ::into_app().try_get_matches_from(itr)?; + let matches = ::into_update_app().try_get_matches_from(itr)?; ::update_from_arg_matches(self, &matches); Ok(()) } @@ -139,6 +138,10 @@ pub trait IntoApp: Sized { fn into_app<'help>() -> App<'help>; /// @TODO @release @docs fn augment_clap(app: App<'_>) -> App<'_>; + /// @TODO @release @docs + fn into_update_app<'help>() -> App<'help>; + /// @TODO @release @docs + fn augment_update_clap(app: App<'_>) -> App<'_>; } /// Converts an instance of [`ArgMatches`] to a consumer defined struct. @@ -192,6 +195,8 @@ pub trait Subcommand: Sized { fn update_from_subcommand(&mut self, name: &str, matches: Option<&ArgMatches>); /// @TODO @release @docs fn augment_subcommands(app: App<'_>) -> App<'_>; + /// @TODO @release @docs + fn augment_update_subcommands(app: App<'_>) -> App<'_>; } /// @TODO @release @docs @@ -238,6 +243,12 @@ impl IntoApp for Box { fn augment_clap(app: App<'_>) -> App<'_> { ::augment_clap(app) } + fn into_update_app<'help>() -> App<'help> { + ::into_update_app() + } + fn augment_update_clap(app: App<'_>) -> App<'_> { + ::augment_update_clap(app) + } } impl FromArgMatches for Box { @@ -259,4 +270,7 @@ impl Subcommand for Box { fn augment_subcommands(app: App<'_>) -> App<'_> { ::augment_subcommands(app) } + fn augment_update_subcommands(app: App<'_>) -> App<'_> { + ::augment_update_subcommands(app) + } } From 5d342a74382975e1fe2dc49fcf1f3a2144428bfa Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Tue, 25 Aug 2020 12:56:26 +0200 Subject: [PATCH 05/11] Update to the new from_subcommand logic --- clap_derive/src/derives/from_arg_matches.rs | 8 +++----- clap_derive/src/derives/subcommand.rs | 12 +++++------- clap_derive/src/dummies.rs | 2 +- src/derive.rs | 6 +++--- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/clap_derive/src/derives/from_arg_matches.rs b/clap_derive/src/derives/from_arg_matches.rs index 32c1c9fc..955173fa 100644 --- a/clap_derive/src/derives/from_arg_matches.rs +++ b/clap_derive/src/derives/from_arg_matches.rs @@ -73,8 +73,7 @@ pub fn gen_for_enum(name: &Ident) -> TokenStream { <#name as ::clap::Subcommand>::from_subcommand(matches.subcommand()).unwrap() } fn update_from_arg_matches(&mut self, matches: &::clap::ArgMatches) { - let (name, subcmd) = matches.subcommand(); - <#name as ::clap::Subcommand>::update_from_subcommand(self, name, subcmd); + <#name as ::clap::Subcommand>::update_from_subcommand(self, matches.subcommand()); } } } @@ -285,7 +284,7 @@ pub fn gen_updater( }; let updater = quote_spanned!{ ty.span()=> - <#subcmd_type as ::clap::Subcommand>::update_from_subcommand(#field_name, name, subcmd); + <#subcmd_type as ::clap::Subcommand>::update_from_subcommand(#field_name, subcmd); }; let updater = match **ty { @@ -294,7 +293,6 @@ pub fn gen_updater( #updater } else { *#field_name = <#subcmd_type as ::clap::Subcommand>::from_subcommand( - name, subcmd ) } @@ -306,7 +304,7 @@ pub fn gen_updater( quote_spanned! { kind.span()=> { - let (name, subcmd) = matches.subcommand(); + let subcmd = matches.subcommand(); #access #updater } diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index 0c26472b..f4b0c43f 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -405,7 +405,7 @@ fn gen_update_from_subcommand( ( quote!((ref mut arg)), quote! { - <#ty as ::clap::Subcommand>::update_from_subcommand(arg, name, sub); + <#ty as ::clap::Subcommand>::update_from_subcommand(arg, Some((name, matches))); }, ) } @@ -422,17 +422,15 @@ fn gen_update_from_subcommand( quote! { fn update_from_subcommand<'b>( &mut self, - name: &'b str, - sub: Option<&'b ::clap::ArgMatches>) - { - if let Some(matches) = sub { + subcommand: Option<(&str, &::clap::ArgMatches)> + ) { + if let Some((name, matches)) = subcommand { match (name, self) { #( #subcommands ),* #( #child_subcommands ),* - (_, s) => if let Some(sub) = ::from_subcommand(name, sub) { + (_, s) => if let Some(sub) = ::from_subcommand(Some((name, matches))) { *s = sub; } - } } } diff --git a/clap_derive/src/dummies.rs b/clap_derive/src/dummies.rs index e78c152d..04a9c988 100644 --- a/clap_derive/src/dummies.rs +++ b/clap_derive/src/dummies.rs @@ -56,7 +56,7 @@ pub fn subcommand(name: &Ident) { fn from_subcommand(_sub: Option<(&str, &::clap::ArgMatches)>) -> Option { unimplemented!() } - fn update_from_subcommand(&mut self, _name: &str, _matches: Option<&::clap::ArgMatches>) { + fn update_from_subcommand(&mut self, _sub: Option<(&str, &::clap::ArgMatches)>) { unimplemented!() } fn augment_subcommands(_app: ::clap::App<'_>) -> ::clap::App<'_> { diff --git a/src/derive.rs b/src/derive.rs index b98b0823..5edde7bd 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -192,7 +192,7 @@ pub trait Subcommand: Sized { /// @TODO @release @docs fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option; /// @TODO @release @docs - fn update_from_subcommand(&mut self, name: &str, matches: Option<&ArgMatches>); + fn update_from_subcommand(&mut self, subcommand: Option<(&str, &ArgMatches)>); /// @TODO @release @docs fn augment_subcommands(app: App<'_>) -> App<'_>; /// @TODO @release @docs @@ -264,8 +264,8 @@ impl Subcommand for Box { fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option { ::from_subcommand(subcommand).map(Box::new) } - fn update_from_subcommand(&mut self, name: &str, matches: Option<&ArgMatches>) { - ::update_from_subcommand(self, name, matches); + fn update_from_subcommand(&mut self, subcommand: Option<(&str, &ArgMatches)>) { + ::update_from_subcommand(self, subcommand); } fn augment_subcommands(app: App<'_>) -> App<'_> { ::augment_subcommands(app) From 9ebb46d65c73fea31fd7124d7328866d398258bb Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Tue, 25 Aug 2020 13:46:03 +0200 Subject: [PATCH 06/11] Test for partial updates --- clap_derive/tests/custom-string-parsers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clap_derive/tests/custom-string-parsers.rs b/clap_derive/tests/custom-string-parsers.rs index ed93013b..32ebf70a 100644 --- a/clap_derive/tests/custom-string-parsers.rs +++ b/clap_derive/tests/custom-string-parsers.rs @@ -142,13 +142,13 @@ fn update_every_custom_parser() { d: "D", }; - opt.update_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"]); + opt.update_from(&["test", "-a=?", "-b=?", "-d=?"]); assert_eq!( NoOpOpt { a: "A", b: "B", - c: "C", + c: "0", d: "D" }, opt From 77e4e65e31611509ee7eebf1500b39fc4ac1b008 Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Sat, 24 Oct 2020 13:13:53 +0200 Subject: [PATCH 07/11] Address new clippy lints --- clap_derive/src/derives/subcommand.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index f4b0c43f..b750753a 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -347,10 +347,7 @@ fn gen_update_from_subcommand( }) .partition(|(_, attrs)| { let kind = attrs.kind(); - match &*kind { - Kind::Flatten => true, - _ => false, - } + matches!(&*kind, Kind::Flatten) }); let subcommands = variants.iter().map(|(variant, attrs)| { From 7a5c12e79ae5e9ef86bf483babdc6bc723ebc148 Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Sun, 8 Nov 2020 11:55:33 +0100 Subject: [PATCH 08/11] Avoid a level of indirection for augment_*subcommand --- clap_derive/src/derives/subcommand.rs | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index b750753a..df6a76c0 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -33,9 +33,10 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr Sp::call_site(DEFAULT_ENV_CASING), ); + let augment_subcommands = gen_augment("augment_subcommands", &e.variants, &attrs, false); + let augment_update_subcommands = + gen_augment("augment_update_subcommands", &e.variants, &attrs, true); let from_subcommand = gen_from_subcommand(name, &e.variants, &attrs); - let augment_subcommands = gen_augment_subcommands(&e.variants, &attrs); - let augment_update_subcommands = gen_augment_update_subcommands(&e.variants, &attrs); let update_from_subcommand = gen_update_from_subcommand(name, &e.variants, &attrs); quote! { @@ -60,25 +61,6 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr } } -fn gen_augment_subcommands( - variants: &Punctuated, - parent_attribute: &Attrs, -) -> TokenStream { - gen_augment("augment_subcommands", variants, parent_attribute, false) -} - -fn gen_augment_update_subcommands( - variants: &Punctuated, - parent_attribute: &Attrs, -) -> TokenStream { - gen_augment( - "augment_update_subcommands", - variants, - parent_attribute, - true, - ) -} - fn gen_augment( fn_name: &str, variants: &Punctuated, From ac3e5f9a8f0cb339149f93df2c99f507acdfba4d Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Sat, 14 Nov 2020 10:53:13 +0100 Subject: [PATCH 09/11] Factor out argument parsing generation --- clap_derive/src/derives/from_arg_matches.rs | 367 ++++++++------------ 1 file changed, 139 insertions(+), 228 deletions(-) diff --git a/clap_derive/src/derives/from_arg_matches.rs b/clap_derive/src/derives/from_arg_matches.rs index 955173fa..a214d1f2 100644 --- a/clap_derive/src/derives/from_arg_matches.rs +++ b/clap_derive/src/derives/from_arg_matches.rs @@ -18,7 +18,7 @@ use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Field, Ident, use crate::{ attrs::{Attrs, Kind, ParserKind}, - utils::{sub_type, subty_if_name, Ty}, + utils::{sub_type, subty_if_name, Sp, Ty}, }; pub fn gen_for_struct( @@ -87,6 +87,142 @@ fn gen_arg_enum_parse(ty: &Type, attrs: &Attrs) -> TokenStream { } } +fn gen_parsers( + attrs: &Attrs, + ty: &Sp, + field_name: &Ident, + field: &Field, + update: Option<&TokenStream>, +) -> TokenStream { + use self::ParserKind::*; + + let parser = attrs.parser(); + let func = &parser.func; + let span = parser.kind.span(); + let (value_of, values_of, mut parse) = match *parser.kind { + FromStr => ( + quote_spanned!(span=> value_of), + quote_spanned!(span=> values_of), + func.clone(), + ), + TryFromStr => ( + quote_spanned!(span=> value_of), + quote_spanned!(span=> values_of), + quote_spanned!(func.span()=> |s| #func(s).unwrap()), + ), + FromOsStr => ( + quote_spanned!(span=> value_of_os), + quote_spanned!(span=> values_of_os), + func.clone(), + ), + TryFromOsStr => ( + quote_spanned!(span=> value_of_os), + quote_spanned!(span=> values_of_os), + quote_spanned!(func.span()=> |s| #func(s).unwrap()), + ), + FromOccurrences => ( + quote_spanned!(span=> occurrences_of), + quote!(), + func.clone(), + ), + FromFlag => (quote!(), quote!(), func.clone()), + }; + + let flag = *attrs.parser().kind == ParserKind::FromFlag; + let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences; + let name = attrs.cased_name(); + + let field_value = match **ty { + Ty::Bool => { + if update.is_some() { + quote_spanned! { ty.span()=> + *#field_name || matches.is_present(#name) + } + } else { + quote_spanned! { ty.span()=> + matches.is_present(#name) + } + } + } + + Ty::Option => { + if attrs.is_enum() { + if let Some(subty) = subty_if_name(&field.ty, "Option") { + parse = gen_arg_enum_parse(subty, &attrs); + } + } + + quote_spanned! { ty.span()=> + matches.#value_of(#name) + .map(#parse) + } + } + + Ty::OptionOption => quote_spanned! { ty.span()=> + if matches.is_present(#name) { + Some(matches.#value_of(#name).map(#parse)) + } else { + None + } + }, + + Ty::OptionVec => quote_spanned! { ty.span()=> + if matches.is_present(#name) { + Some(matches.#values_of(#name) + .map(|v| v.map(#parse).collect()) + .unwrap_or_else(Vec::new)) + } else { + None + } + }, + + Ty::Vec => { + if attrs.is_enum() { + if let Some(subty) = subty_if_name(&field.ty, "Vec") { + parse = gen_arg_enum_parse(subty, &attrs); + } + } + + quote_spanned! { ty.span()=> + matches.#values_of(#name) + .map(|v| v.map(#parse).collect()) + .unwrap_or_else(Vec::new) + } + } + + Ty::Other if occurrences => quote_spanned! { ty.span()=> + #parse(matches.#value_of(#name)) + }, + + Ty::Other if flag => quote_spanned! { ty.span()=> + #parse(matches.is_present(#name)) + }, + + Ty::Other => { + if attrs.is_enum() { + parse = gen_arg_enum_parse(&field.ty, &attrs); + } + + quote_spanned! { ty.span()=> + matches.#value_of(#name) + .map(#parse) + .unwrap() + } + } + }; + + if let Some(access) = update { + quote_spanned! { field.span()=> + if matches.is_present(#name) { + #access + *#field_name = #field_value + } + } + } else { + quote_spanned!(field.span()=> #field_name: #field_value ) + } +} + pub fn gen_constructor(fields: &Punctuated, parent_attribute: &Attrs) -> TokenStream { let fields = fields.iter().map(|field| { let attrs = Attrs::from_field( @@ -128,118 +264,7 @@ pub fn gen_constructor(fields: &Punctuated, parent_attribute: &Att Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()), }, - Kind::Arg(ty) => { - use self::ParserKind::*; - - let parser = attrs.parser(); - let func = &parser.func; - let span = parser.kind.span(); - let (value_of, values_of, mut parse) = match *parser.kind { - FromStr => ( - quote_spanned!(span=> value_of), - quote_spanned!(span=> values_of), - func.clone(), - ), - TryFromStr => ( - quote_spanned!(span=> value_of), - quote_spanned!(span=> values_of), - quote_spanned!(func.span()=> |s| #func(s).unwrap()), - ), - FromOsStr => ( - quote_spanned!(span=> value_of_os), - quote_spanned!(span=> values_of_os), - func.clone(), - ), - TryFromOsStr => ( - quote_spanned!(span=> value_of_os), - quote_spanned!(span=> values_of_os), - quote_spanned!(func.span()=> |s| #func(s).unwrap()), - ), - FromOccurrences => ( - quote_spanned!(span=> occurrences_of), - quote!(), - func.clone(), - ), - FromFlag => (quote!(), quote!(), func.clone()), - }; - - let flag = *attrs.parser().kind == ParserKind::FromFlag; - let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences; - let name = attrs.cased_name(); - - let field_value = match **ty { - Ty::Bool => quote_spanned! { ty.span()=> - matches.is_present(#name) - }, - - Ty::Option => { - if attrs.is_enum() { - if let Some(subty) = subty_if_name(&field.ty, "Option") { - parse = gen_arg_enum_parse(subty, &attrs); - } - } - - quote_spanned! { ty.span()=> - matches.#value_of(#name) - .map(#parse) - } - } - - Ty::OptionOption => quote_spanned! { ty.span()=> - if matches.is_present(#name) { - Some(matches.#value_of(#name).map(#parse)) - } else { - None - } - }, - - Ty::OptionVec => quote_spanned! { ty.span()=> - if matches.is_present(#name) { - Some(matches.#values_of(#name) - .map(|v| v.map(#parse).collect()) - .unwrap_or_else(Vec::new)) - } else { - None - } - }, - - Ty::Vec => { - if attrs.is_enum() { - if let Some(subty) = subty_if_name(&field.ty, "Vec") { - parse = gen_arg_enum_parse(subty, &attrs); - } - } - - quote_spanned! { ty.span()=> - matches.#values_of(#name) - .map(|v| v.map(#parse).collect()) - .unwrap_or_else(Vec::new) - } - } - - Ty::Other if occurrences => quote_spanned! { ty.span()=> - #parse(matches.#value_of(#name)) - }, - - Ty::Other if flag => quote_spanned! { ty.span()=> - #parse(matches.is_present(#name)) - }, - - Ty::Other => { - if attrs.is_enum() { - parse = gen_arg_enum_parse(&field.ty, &attrs); - } - - quote_spanned! { ty.span()=> - matches.#value_of(#name) - .map(#parse) - .unwrap() - } - } - }; - - quote_spanned!(field.span()=> #field_name: #field_value ) - } + Kind::Arg(ty) => gen_parsers(&attrs, ty, field_name, field, None), } }); @@ -320,121 +345,7 @@ pub fn gen_updater( Kind::Skip(_) => quote!(), Kind::Arg(ty) => { - use self::ParserKind::*; - - let parser = attrs.parser(); - let func = &parser.func; - let span = parser.kind.span(); - let (value_of, values_of, mut parse) = match *parser.kind { - FromStr => ( - quote_spanned!(span=> value_of), - quote_spanned!(span=> values_of), - func.clone(), - ), - TryFromStr => ( - quote_spanned!(span=> value_of), - quote_spanned!(span=> values_of), - quote_spanned!(func.span()=> |s| #func(s).unwrap()), - ), - FromOsStr => ( - quote_spanned!(span=> value_of_os), - quote_spanned!(span=> values_of_os), - func.clone(), - ), - TryFromOsStr => ( - quote_spanned!(span=> value_of_os), - quote_spanned!(span=> values_of_os), - quote_spanned!(func.span()=> |s| #func(s).unwrap()), - ), - FromOccurrences => ( - quote_spanned!(span=> occurrences_of), - quote!(), - func.clone(), - ), - FromFlag => (quote!(), quote!(), func.clone()), - }; - - let flag = *attrs.parser().kind == ParserKind::FromFlag; - let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences; - let name = attrs.cased_name(); - - let field_value = match **ty { - Ty::Bool => quote_spanned! { ty.span()=> - *#field_name || matches.is_present(#name) - }, - - Ty::Option => { - if attrs.is_enum() { - if let Some(subty) = sub_type(&field.ty) { - parse = gen_arg_enum_parse(subty, &attrs); - } - } - - quote_spanned! { ty.span()=> - matches.#value_of(#name) - .map(#parse) - } - } - - Ty::OptionOption => quote_spanned! { ty.span()=> - if matches.is_present(#name) { - Some(matches.#value_of(#name).map(#parse)) - } else { - None - } - }, - - Ty::OptionVec => quote_spanned! { ty.span()=> - if matches.is_present(#name) { - Some(matches.#values_of(#name) - .map(|v| v.map(#parse).collect()) - .unwrap_or_else(Vec::new)) - } else { - None - } - }, - - Ty::Vec => { - if attrs.is_enum() { - if let Some(subty) = subty_if_name(&field.ty, "Vec") { - parse = gen_arg_enum_parse(subty, &attrs); - } - } - - quote_spanned! { ty.span()=> - matches.#values_of(#name) - .map(|v| v.map(#parse).collect()) - .unwrap_or_else(Vec::new) - } - } - - Ty::Other if occurrences => quote_spanned! { ty.span()=> - #parse(matches.#value_of(#name)) - }, - - Ty::Other if flag => quote_spanned! { ty.span()=> - #parse(matches.is_present(#name)) - }, - - Ty::Other => { - if attrs.is_enum() { - parse = gen_arg_enum_parse(&field.ty, &attrs); - } - - quote_spanned! { ty.span()=> - matches.#value_of(#name) - .map(#parse) - .unwrap() - } - } - }; - - quote_spanned! { field.span()=> - if matches.is_present(#name) { - #access - *#field_name = #field_value - } - } + gen_parsers(&attrs, ty, field_name, field, Some(&access)) } } }); From 3e51839383c3d0f92301b94dfd14032d4149eabf Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Sat, 14 Nov 2020 10:58:30 +0100 Subject: [PATCH 10/11] Rename the update trait methods --- clap_derive/src/derives/into_app.rs | 16 ++++++++-------- clap_derive/src/derives/subcommand.rs | 6 +++--- clap_derive/src/dummies.rs | 6 +++--- src/derive.rs | 22 +++++++++++----------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/clap_derive/src/derives/into_app.rs b/clap_derive/src/derives/into_app.rs index f879054a..b9fb2247 100644 --- a/clap_derive/src/derives/into_app.rs +++ b/clap_derive/src/derives/into_app.rs @@ -104,13 +104,13 @@ pub fn gen_for_enum(name: &Ident) -> TokenStream { <#name as ::clap::Subcommand>::augment_subcommands(app) } - fn into_update_app<'b>() -> ::clap::App<'b> { + fn into_app_for_update<'b>() -> ::clap::App<'b> { let app = ::clap::App::new(#app_name); - <#name as ::clap::IntoApp>::augment_update_clap(app) + <#name as ::clap::IntoApp>::augment_clap_for_update(app) } - fn augment_update_clap<'b>(app: ::clap::App<'b>) -> ::clap::App<'b> { - <#name as ::clap::Subcommand>::augment_update_subcommands(app) + fn augment_clap_for_update<'b>(app: ::clap::App<'b>) -> ::clap::App<'b> { + <#name as ::clap::Subcommand>::augment_subcommands_for_update(app) } } } @@ -132,8 +132,8 @@ fn gen_into_app_fn(attrs: &[Attribute]) -> GenOutput { fn into_app<'b>() -> ::clap::App<'b> { Self::augment_clap(::clap::App::new(#name)) } - fn into_update_app<'b>() -> ::clap::App<'b> { - Self::augment_update_clap(::clap::App::new(#name)) + fn into_app_for_update<'b>() -> ::clap::App<'b> { + Self::augment_clap_for_update(::clap::App::new(#name)) } }; @@ -148,7 +148,7 @@ fn gen_augment_clap_fn(fields: &Punctuated, parent_attribute: &Att fn augment_clap<'b>(#app_var: ::clap::App<'b>) -> ::clap::App<'b> { #augmentation } - fn augment_update_clap<'b>(#app_var: ::clap::App<'b>) -> ::clap::App<'b> { + fn augment_clap_for_update<'b>(#app_var: ::clap::App<'b>) -> ::clap::App<'b> { #augmentation_update } } @@ -193,7 +193,7 @@ pub fn gen_app_augmentation( let span = field.span(); let ts = if override_required { quote! { - let #app_var = <#subcmd_type as ::clap::Subcommand>::augment_update_subcommands( #app_var ); + let #app_var = <#subcmd_type as ::clap::Subcommand>::augment_subcommands_for_update( #app_var ); } } else{ quote! { diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index df6a76c0..5631a85b 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -34,8 +34,8 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr ); let augment_subcommands = gen_augment("augment_subcommands", &e.variants, &attrs, false); - let augment_update_subcommands = - gen_augment("augment_update_subcommands", &e.variants, &attrs, true); + let augment_subcommands_for_update = + gen_augment("augment_subcommands_for_update", &e.variants, &attrs, true); let from_subcommand = gen_from_subcommand(name, &e.variants, &attrs); let update_from_subcommand = gen_update_from_subcommand(name, &e.variants, &attrs); @@ -55,7 +55,7 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr impl ::clap::Subcommand for #name { #augment_subcommands #from_subcommand - #augment_update_subcommands + #augment_subcommands_for_update #update_from_subcommand } } diff --git a/clap_derive/src/dummies.rs b/clap_derive/src/dummies.rs index 04a9c988..596d822a 100644 --- a/clap_derive/src/dummies.rs +++ b/clap_derive/src/dummies.rs @@ -27,10 +27,10 @@ pub fn into_app(name: &Ident) { fn augment_clap<'b>(_app: ::clap::App<'b>) -> ::clap::App<'b> { unimplemented!() } - fn into_update_app<'b>() -> ::clap::App<'b> { + fn into_app_for_update<'b>() -> ::clap::App<'b> { unimplemented!() } - fn augment_update_clap<'b>(_app: ::clap::App<'b>) -> ::clap::App<'b> { + fn augment_clap_for_update<'b>(_app: ::clap::App<'b>) -> ::clap::App<'b> { unimplemented!() } } @@ -62,7 +62,7 @@ pub fn subcommand(name: &Ident) { fn augment_subcommands(_app: ::clap::App<'_>) -> ::clap::App<'_> { unimplemented!() } - fn augment_update_subcommands(_app: ::clap::App<'_>) -> ::clap::App<'_> { + fn augment_subcommands_for_update(_app: ::clap::App<'_>) -> ::clap::App<'_> { unimplemented!() } } diff --git a/src/derive.rs b/src/derive.rs index 5edde7bd..d456a892 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -113,7 +113,7 @@ pub trait Clap: FromArgMatches + IntoApp + Sized { T: Into + Clone, { // TODO find a way to get partial matches - let matches = ::into_update_app().get_matches_from(itr); + let matches = ::into_app_for_update().get_matches_from(itr); ::update_from_arg_matches(self, &matches); } @@ -124,7 +124,7 @@ pub trait Clap: FromArgMatches + IntoApp + Sized { // TODO (@CreepySkeleton): discover a way to avoid cloning here T: Into + Clone, { - let matches = ::into_update_app().try_get_matches_from(itr)?; + let matches = ::into_app_for_update().try_get_matches_from(itr)?; ::update_from_arg_matches(self, &matches); Ok(()) } @@ -139,9 +139,9 @@ pub trait IntoApp: Sized { /// @TODO @release @docs fn augment_clap(app: App<'_>) -> App<'_>; /// @TODO @release @docs - fn into_update_app<'help>() -> App<'help>; + fn into_app_for_update<'help>() -> App<'help>; /// @TODO @release @docs - fn augment_update_clap(app: App<'_>) -> App<'_>; + fn augment_clap_for_update(app: App<'_>) -> App<'_>; } /// Converts an instance of [`ArgMatches`] to a consumer defined struct. @@ -196,7 +196,7 @@ pub trait Subcommand: Sized { /// @TODO @release @docs fn augment_subcommands(app: App<'_>) -> App<'_>; /// @TODO @release @docs - fn augment_update_subcommands(app: App<'_>) -> App<'_>; + fn augment_subcommands_for_update(app: App<'_>) -> App<'_>; } /// @TODO @release @docs @@ -243,11 +243,11 @@ impl IntoApp for Box { fn augment_clap(app: App<'_>) -> App<'_> { ::augment_clap(app) } - fn into_update_app<'help>() -> App<'help> { - ::into_update_app() + fn into_app_for_update<'help>() -> App<'help> { + ::into_app_for_update() } - fn augment_update_clap(app: App<'_>) -> App<'_> { - ::augment_update_clap(app) + fn augment_clap_for_update(app: App<'_>) -> App<'_> { + ::augment_clap_for_update(app) } } @@ -270,7 +270,7 @@ impl Subcommand for Box { fn augment_subcommands(app: App<'_>) -> App<'_> { ::augment_subcommands(app) } - fn augment_update_subcommands(app: App<'_>) -> App<'_> { - ::augment_update_subcommands(app) + fn augment_subcommands_for_update(app: App<'_>) -> App<'_> { + ::augment_subcommands_for_update(app) } } From d18ff583914e39b924542e5705c8d605c3943b9e Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Sat, 14 Nov 2020 11:09:39 +0100 Subject: [PATCH 11/11] Add a skip test --- clap_derive/tests/skip.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clap_derive/tests/skip.rs b/clap_derive/tests/skip.rs index 9dcb2ca2..81cded34 100644 --- a/clap_derive/tests/skip.rs +++ b/clap_derive/tests/skip.rs @@ -19,13 +19,20 @@ fn skip_1() { } assert!(Opt::try_parse_from(&["test", "-x", "10", "20"]).is_err()); + + let mut opt = Opt::parse_from(&["test", "-x", "10"]); assert_eq!( - Opt::parse_from(&["test", "-x", "10"]), + opt, Opt { x: 10, s: 0, // default } ); + opt.s = 42; + + opt.update_from(&["test", "-x", "22"]); + + assert_eq!(opt, Opt { x: 22, s: 42 }); } #[test]