From 2ed4f0dbe5bfbc944dc8bb0bf9045c907ead6e32 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 15 Jan 2020 16:34:47 +0100 Subject: [PATCH 01/19] Implemented: flatten on enums Fixes #327 --- structopt-derive/src/attrs.rs | 2 +- structopt-derive/src/lib.rs | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/structopt-derive/src/attrs.rs b/structopt-derive/src/attrs.rs index 411bb6a4..1f2d36a9 100644 --- a/structopt-derive/src/attrs.rs +++ b/structopt-derive/src/attrs.rs @@ -401,7 +401,7 @@ impl Attrs { } match &*res.kind { Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"), - Kind::FlattenStruct => abort!(res.kind.span(), "flatten is only allowed on fields"), + Kind::FlattenStruct => res, Kind::Skip(_) => abort!(res.kind.span(), "skip is only allowed on fields"), Kind::Arg(_) => res, } diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 89720998..7cf8b948 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -485,12 +485,28 @@ fn gen_augment_clap_enum( let name = attrs.cased_name(); let from_attrs = attrs.top_level_methods(); let version = attrs.version(); - quote! { - .subcommand({ - let #app_var = ::structopt::clap::SubCommand::with_name(#name); - let #app_var = #arg_block; - #app_var#from_attrs#version - }) + let kind = attrs.kind(); + match &*kind { + Kind::FlattenStruct => { + quote! { + .subcommands({ + let name2 = #name; + let #app_var = ::structopt::clap::SubCommand::with_name(#name); + let #app_var = #arg_block; + #app_var#from_attrs#version.p.subcommands + }) + } + }, + _ => { + quote! { + .subcommand({ + let name1 = #name; + let #app_var = ::structopt::clap::SubCommand::with_name(#name); + let #app_var = #arg_block; + #app_var#from_attrs#version + }) + } + }, } }); From 7b1892865195f76d211922b357a1a54e28472252 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 15 Jan 2020 16:38:32 +0100 Subject: [PATCH 02/19] Clean-up --- structopt-derive/src/attrs.rs | 3 +-- structopt-derive/src/lib.rs | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/structopt-derive/src/attrs.rs b/structopt-derive/src/attrs.rs index 1f2d36a9..540792e0 100644 --- a/structopt-derive/src/attrs.rs +++ b/structopt-derive/src/attrs.rs @@ -401,9 +401,8 @@ impl Attrs { } match &*res.kind { Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"), - Kind::FlattenStruct => res, Kind::Skip(_) => abort!(res.kind.span(), "skip is only allowed on fields"), - Kind::Arg(_) => res, + Kind::Arg(_) | Kind::FlattenStruct => res, } } diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 7cf8b948..21600c29 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -490,7 +490,6 @@ fn gen_augment_clap_enum( Kind::FlattenStruct => { quote! { .subcommands({ - let name2 = #name; let #app_var = ::structopt::clap::SubCommand::with_name(#name); let #app_var = #arg_block; #app_var#from_attrs#version.p.subcommands @@ -500,7 +499,6 @@ fn gen_augment_clap_enum( _ => { quote! { .subcommand({ - let name1 = #name; let #app_var = ::structopt::clap::SubCommand::with_name(#name); let #app_var = #arg_block; #app_var#from_attrs#version From 41018b55517310509993cf9e3e4904fdf655474a Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 12:38:22 +0100 Subject: [PATCH 03/19] WIP --- structopt-derive/src/lib.rs | 55 +++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 21600c29..f586aed7 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -458,14 +458,20 @@ fn gen_augment_clap_enum( parent_attribute.casing(), parent_attribute.env_casing(), ); + let kind = attrs.kind(); let app_var = Ident::new("subcommand", Span::call_site()); let arg_block = match variant.fields { Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs), Unit => quote!( #app_var ), Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { let ty = &unnamed[0]; + let kind = match &*kind { + Kind::FlattenStruct => "FlattenStruct", + _ => "other", + }; quote_spanned! { ty.span()=> { + let kind = #kind; let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap( #app_var ); @@ -485,14 +491,13 @@ fn gen_augment_clap_enum( let name = attrs.cased_name(); let from_attrs = attrs.top_level_methods(); let version = attrs.version(); - let kind = attrs.kind(); match &*kind { Kind::FlattenStruct => { quote! { .subcommands({ let #app_var = ::structopt::clap::SubCommand::with_name(#name); let #app_var = #arg_block; - #app_var#from_attrs#version.p.subcommands + #app_var#from_attrs#version }) } }, @@ -544,23 +549,39 @@ fn gen_from_subcommand( parent_attribute.casing(), parent_attribute.env_casing(), ); - let sub_name = attrs.cased_name(); - let variant_name = &variant.ident; - let constructor_block = match variant.fields { - Named(ref fields) => gen_constructor(&fields.named, &attrs), - Unit => quote!(), - Unnamed(ref fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed[0]; - quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) - } - Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), - }; + let kind = attrs.kind(); + match &*kind { + /* + Kind::FlattenStruct => { + vec![quote! {}] + }, + */ + _ => { + let sub_name = attrs.cased_name(); + let variant_name = &variant.ident; + let constructor_block = match variant.fields { + Named(ref fields) => gen_constructor(&fields.named, &attrs), + Unit => quote!(), + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) + } + Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), + }; - quote! { - (#sub_name, Some(matches)) => - Some(#name :: #variant_name #constructor_block) + let kind = match &*kind { + Kind::FlattenStruct => "FlattenStruct", + _ => "other", + }; + vec![quote! { + (#sub_name, Some(matches)) => { + let kind = #kind; + Some(#name :: #variant_name #constructor_block) + } + }] + }, } - }); + }).flatten(); quote! { fn from_subcommand<'a, 'b>( From 03bbedf29ea23971162bdbeb1518d83d76e57ac1 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 14:05:51 +0100 Subject: [PATCH 04/19] WIP --- structopt-derive/src/lib.rs | 88 +++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index f586aed7..d11d69cf 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -449,7 +449,7 @@ fn gen_augment_clap_enum( ) -> TokenStream { use syn::Fields::*; - let subcommands = variants.iter().map(|variant| { + let subcommands = variants.iter().filter_map(|variant| { let attrs = Attrs::from_struct( variant.span(), &variant.attrs, @@ -492,34 +492,53 @@ fn gen_augment_clap_enum( let from_attrs = attrs.top_level_methods(); let version = attrs.version(); match &*kind { - Kind::FlattenStruct => { - quote! { - .subcommands({ - let #app_var = ::structopt::clap::SubCommand::with_name(#name); - let #app_var = #arg_block; - #app_var#from_attrs#version - }) - } - }, + Kind::FlattenStruct => None, _ => { - quote! { + Some(quote! { .subcommand({ let #app_var = ::structopt::clap::SubCommand::with_name(#name); let #app_var = #arg_block; #app_var#from_attrs#version }) - } + }) }, } }); let app_methods = parent_attribute.top_level_methods(); let version = parent_attribute.version(); + let augment_claps = variants.iter().filter_map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + Some(parent_attribute), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + let kind = attrs.kind(); + match &*kind { + Kind::FlattenStruct => { + match variant.fields { + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0]; + Some(quote! { + let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app); + }) + } + _ => todo!(), + } + }, + _ => None, + } + }); quote! { fn augment_clap<'a, 'b>( app: ::structopt::clap::App<'a, 'b> ) -> ::structopt::clap::App<'a, 'b> { - app #app_methods #( #subcommands )* #version + let app = app #app_methods #( #subcommands )* #version; + #( #augment_claps )*; + app } } } @@ -540,7 +559,7 @@ fn gen_from_subcommand( ) -> TokenStream { use syn::Fields::*; - let match_arms = variants.iter().map(|variant| { + let match_arms = variants.iter().filter_map(|variant| { let attrs = Attrs::from_struct( variant.span(), &variant.attrs, @@ -551,11 +570,7 @@ fn gen_from_subcommand( ); let kind = attrs.kind(); match &*kind { - /* - Kind::FlattenStruct => { - vec![quote! {}] - }, - */ + Kind::FlattenStruct => None, _ => { let sub_name = attrs.cased_name(); let variant_name = &variant.ident; @@ -573,20 +588,49 @@ fn gen_from_subcommand( Kind::FlattenStruct => "FlattenStruct", _ => "other", }; - vec![quote! { + Some(quote! { (#sub_name, Some(matches)) => { let kind = #kind; Some(#name :: #variant_name #constructor_block) } - }] + }) }, } - }).flatten(); + }); + let child_subcommands = variants.iter().filter_map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + Some(parent_attribute), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + let kind = attrs.kind(); + match &*kind { + Kind::FlattenStruct => { + let variant_name = &variant.ident; + match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + Some(quote! { + if let Some(res) = <#ty as ::structopt::StructOptInternal>::from_subcommand(sub) { + return Some(#name :: #variant_name (res)); + } + }) + }, + _ => todo!(), + } + }, + _ => None, + } + }); quote! { fn from_subcommand<'a, 'b>( sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>) ) -> Option { + #( #child_subcommands )*; match sub { #( #match_arms ),*, _ => None From fdc18cbb57aadb9ce986ea2e1d63f300f2eafa46 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 14:22:38 +0100 Subject: [PATCH 05/19] Some clean-up --- structopt-derive/src/lib.rs | 188 ++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 107 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index d11d69cf..8b55502e 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -449,29 +449,35 @@ fn gen_augment_clap_enum( ) -> TokenStream { use syn::Fields::*; - let subcommands = variants.iter().filter_map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - Some(parent_attribute), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let kind = attrs.kind(); + let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants.iter() + .map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + Some(parent_attribute), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + (variant, attrs) + }) + .partition(|(_, attrs)| { + let kind = attrs.kind(); + match &*kind { + Kind::FlattenStruct => true, + _ => false + } + }); + + let subcommands = variants.iter().map(|(variant, attrs)| { let app_var = Ident::new("subcommand", Span::call_site()); let arg_block = match variant.fields { Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs), Unit => quote!( #app_var ), Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { let ty = &unnamed[0]; - let kind = match &*kind { - Kind::FlattenStruct => "FlattenStruct", - _ => "other", - }; quote_spanned! { ty.span()=> { - let kind = #kind; let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap( #app_var ); @@ -491,45 +497,26 @@ fn gen_augment_clap_enum( let name = attrs.cased_name(); let from_attrs = attrs.top_level_methods(); let version = attrs.version(); - match &*kind { - Kind::FlattenStruct => None, - _ => { - Some(quote! { - .subcommand({ - let #app_var = ::structopt::clap::SubCommand::with_name(#name); - let #app_var = #arg_block; - #app_var#from_attrs#version - }) - }) - }, + quote! { + .subcommand({ + let #app_var = ::structopt::clap::SubCommand::with_name(#name); + let #app_var = #arg_block; + #app_var#from_attrs#version + }) } }); let app_methods = parent_attribute.top_level_methods(); let version = parent_attribute.version(); - let augment_claps = variants.iter().filter_map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - Some(parent_attribute), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let kind = attrs.kind(); - match &*kind { - Kind::FlattenStruct => { - match variant.fields { - Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { - let ty = &unnamed[0]; - Some(quote! { - let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app); - }) - } - _ => todo!(), + let augment_claps = flatten_variants.iter().map(|(variant, _attrs)| { + match variant.fields { + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0]; + quote! { + let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app); } - }, - _ => None, + } + _ => todo!(), } }); quote! { @@ -559,70 +546,57 @@ fn gen_from_subcommand( ) -> TokenStream { use syn::Fields::*; - let match_arms = variants.iter().filter_map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - Some(parent_attribute), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let kind = attrs.kind(); - match &*kind { - Kind::FlattenStruct => None, - _ => { - let sub_name = attrs.cased_name(); - let variant_name = &variant.ident; - let constructor_block = match variant.fields { - Named(ref fields) => gen_constructor(&fields.named, &attrs), - Unit => quote!(), - Unnamed(ref fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed[0]; - quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) - } - Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), - }; + let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants.iter() + .map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + Some(parent_attribute), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + (variant, attrs) + }) + .partition(|(_, attrs)| { + let kind = attrs.kind(); + match &*kind { + Kind::FlattenStruct => true, + _ => false + } + }); + + let match_arms = variants.iter().map(|(variant, attrs)| { + let sub_name = attrs.cased_name(); + let variant_name = &variant.ident; + let constructor_block = match variant.fields { + Named(ref fields) => gen_constructor(&fields.named, &attrs), + Unit => quote!(), + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) + } + Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), + }; - let kind = match &*kind { - Kind::FlattenStruct => "FlattenStruct", - _ => "other", - }; - Some(quote! { - (#sub_name, Some(matches)) => { - let kind = #kind; - Some(#name :: #variant_name #constructor_block) - } - }) - }, + quote! { + (#sub_name, Some(matches)) => { + Some(#name :: #variant_name #constructor_block) + } } }); - let child_subcommands = variants.iter().filter_map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - Some(parent_attribute), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let kind = attrs.kind(); - match &*kind { - Kind::FlattenStruct => { - let variant_name = &variant.ident; - match variant.fields { - Unnamed(ref fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed[0]; - Some(quote! { - if let Some(res) = <#ty as ::structopt::StructOptInternal>::from_subcommand(sub) { - return Some(#name :: #variant_name (res)); - } - }) - }, - _ => todo!(), + let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| { + let variant_name = &variant.ident; + match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + quote! { + if let Some(res) = <#ty as ::structopt::StructOptInternal>::from_subcommand(sub) { + return Some(#name :: #variant_name (res)); + } } }, - _ => None, + _ => todo!(), } }); From 57240a5fc09c5bb91abea405862ce13f914db8ce Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 14:58:24 +0100 Subject: [PATCH 06/19] WIP --- structopt-derive/src/attrs.rs | 8 +-- structopt-derive/src/lib.rs | 123 ++++++++++++++++------------------ 2 files changed, 62 insertions(+), 69 deletions(-) diff --git a/structopt-derive/src/attrs.rs b/structopt-derive/src/attrs.rs index 540792e0..3e7b6a30 100644 --- a/structopt-derive/src/attrs.rs +++ b/structopt-derive/src/attrs.rs @@ -23,7 +23,7 @@ use syn::{ pub enum Kind { Arg(Sp), Subcommand(Sp), - FlattenStruct, + Flatten, Skip(Option), } @@ -277,7 +277,7 @@ impl Attrs { } Flatten(ident) => { - let kind = Sp::new(Kind::FlattenStruct, ident.span()); + let kind = Sp::new(Kind::Flatten, ident.span()); self.set_kind(kind); } @@ -402,7 +402,7 @@ impl Attrs { match &*res.kind { Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"), Kind::Skip(_) => abort!(res.kind.span(), "skip is only allowed on fields"), - Kind::Arg(_) | Kind::FlattenStruct => res, + Kind::Arg(_) | Kind::Flatten => res, } } @@ -425,7 +425,7 @@ impl Attrs { res.push_attrs(&field.attrs); match &*res.kind { - Kind::FlattenStruct => { + Kind::Flatten => { if res.has_custom_parser { abort!( res.parser.span(), diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 8b55502e..5774553e 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -117,7 +117,7 @@ fn gen_augmentation( let kind = attrs.kind(); match &*kind { Kind::Subcommand(_) | Kind::Skip(_) => None, - Kind::FlattenStruct => { + Kind::Flatten => { let ty = &field.ty; Some(quote_spanned! { kind.span()=> let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(#app_var); @@ -261,7 +261,7 @@ fn gen_constructor(fields: &Punctuated, parent_attribute: &Attrs) } } - Kind::FlattenStruct => quote_spanned! { kind.span()=> + Kind::Flatten => quote_spanned! { kind.span()=> #field_name: ::structopt::StructOpt::from_clap(matches) }, @@ -449,82 +449,75 @@ fn gen_augment_clap_enum( ) -> TokenStream { use syn::Fields::*; - let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants.iter() - .map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - Some(parent_attribute), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - (variant, attrs) - }) - .partition(|(_, attrs)| { - let kind = attrs.kind(); - match &*kind { - Kind::FlattenStruct => true, - _ => false - } - }); - - let subcommands = variants.iter().map(|(variant, attrs)| { - let app_var = Ident::new("subcommand", Span::call_site()); - let arg_block = match variant.fields { - Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs), - Unit => quote!( #app_var ), - Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { - let ty = &unnamed[0]; - quote_spanned! { ty.span()=> - { - let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap( - #app_var - ); - if <#ty as ::structopt::StructOptInternal>::is_subcommand() { - #app_var.setting( - ::structopt::clap::AppSettings::SubcommandRequiredElseHelp - ) - } else { - #app_var + let subcommands = variants.iter().map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + Some(parent_attribute), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + let kind = attrs.kind(); + match &*kind { + Kind::Flatten => { + match variant.fields { + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0]; + quote! { + let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app); } } + _ => todo!(), } - } - Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), - }; + }, + _ => { + let app_var = Ident::new("subcommand", Span::call_site()); + let arg_block = match variant.fields { + Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs), + Unit => quote!( #app_var ), + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0]; + quote_spanned! { ty.span()=> + { + let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap( + #app_var + ); + if <#ty as ::structopt::StructOptInternal>::is_subcommand() { + #app_var.setting( + ::structopt::clap::AppSettings::SubcommandRequiredElseHelp + ) + } else { + #app_var + } + } + } + } + Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), + }; - let name = attrs.cased_name(); - let from_attrs = attrs.top_level_methods(); - let version = attrs.version(); - quote! { - .subcommand({ - let #app_var = ::structopt::clap::SubCommand::with_name(#name); - let #app_var = #arg_block; - #app_var#from_attrs#version - }) + let name = attrs.cased_name(); + let from_attrs = attrs.top_level_methods(); + let version = attrs.version(); + quote! { + let app = app.subcommand({ + let #app_var = ::structopt::clap::SubCommand::with_name(#name); + let #app_var = #arg_block; + #app_var#from_attrs#version + }); + } + }, } }); let app_methods = parent_attribute.top_level_methods(); let version = parent_attribute.version(); - let augment_claps = flatten_variants.iter().map(|(variant, _attrs)| { - match variant.fields { - Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { - let ty = &unnamed[0]; - quote! { - let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app); - } - } - _ => todo!(), - } - }); quote! { fn augment_clap<'a, 'b>( app: ::structopt::clap::App<'a, 'b> ) -> ::structopt::clap::App<'a, 'b> { - let app = app #app_methods #( #subcommands )* #version; - #( #augment_claps )*; + let app = app #app_methods #version; + #( #subcommands )*; app } } From b678b051ca21ee7e07cac46061b0b66fd7757290 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 15:03:28 +0100 Subject: [PATCH 07/19] WIP --- structopt-derive/src/lib.rs | 89 ++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 5774553e..fc8d6388 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -539,57 +539,49 @@ fn gen_from_subcommand( ) -> TokenStream { use syn::Fields::*; - let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants.iter() - .map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - Some(parent_attribute), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - (variant, attrs) - }) - .partition(|(_, attrs)| { - let kind = attrs.kind(); - match &*kind { - Kind::FlattenStruct => true, - _ => false - } - }); - - let match_arms = variants.iter().map(|(variant, attrs)| { - let sub_name = attrs.cased_name(); + let matches = variants.iter().map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + Some(parent_attribute), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + let kind = attrs.kind(); let variant_name = &variant.ident; - let constructor_block = match variant.fields { - Named(ref fields) => gen_constructor(&fields.named, &attrs), - Unit => quote!(), - Unnamed(ref fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed[0]; - quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) - } - Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), - }; + match &*kind { + Kind::Flatten => { + match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + quote! { + if let Some(res) = <#ty as ::structopt::StructOptInternal>::from_subcommand(sub) { + return Some(#name :: #variant_name (res)); + } + } + }, + _ => todo!(), + } + }, + _ => { + let sub_name = attrs.cased_name(); + let constructor_block = match variant.fields { + Named(ref fields) => gen_constructor(&fields.named, &attrs), + Unit => quote!(), + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) + } + Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), + }; - quote! { - (#sub_name, Some(matches)) => { - Some(#name :: #variant_name #constructor_block) - } - } - }); - let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| { - let variant_name = &variant.ident; - match variant.fields { - Unnamed(ref fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed[0]; quote! { - if let Some(res) = <#ty as ::structopt::StructOptInternal>::from_subcommand(sub) { - return Some(#name :: #variant_name (res)); + if let (#sub_name, Some(matches)) = sub { + return Some(#name :: #variant_name #constructor_block); } } }, - _ => todo!(), } }); @@ -597,11 +589,8 @@ fn gen_from_subcommand( fn from_subcommand<'a, 'b>( sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>) ) -> Option { - #( #child_subcommands )*; - match sub { - #( #match_arms ),*, - _ => None - } + #( #matches )*; + None } } } From 70a5d6d5c4a6484c42f3aa8aeac87c304f0d92b1 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 15:17:22 +0100 Subject: [PATCH 08/19] added test --- tests/flatten.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/flatten.rs b/tests/flatten.rs index 4983d86f..f01e44e7 100644 --- a/tests/flatten.rs +++ b/tests/flatten.rs @@ -93,3 +93,37 @@ fn flatten_in_subcommand() { Opt::from_iter(&["test", "add", "-i", "43"]) ); } + +#[test] +fn merge_subcommands_with_flatten() { + #[derive(StructOpt, PartialEq, Debug)] + enum BaseCli { + Command1(Command1), + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Command1 { + arg1: i32, + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Command2 { + arg2: i32, + } + + #[derive(StructOpt, PartialEq, Debug)] + enum Opt { + #[structopt(flatten)] + BaseCli(BaseCli), + Command2(Command2), + } + + assert_eq!( + Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 42 })), + Opt::from_iter(&["test", "command1", "42"]) + ); + assert_eq!( + Opt::Command2(Command2 { arg2: 43 }), + Opt::from_iter(&["test", "command2", "43"]) + ); +} From ec8ef8f3f4a44ca7e96be056ce87307ed93c9851 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 15:52:47 +0100 Subject: [PATCH 09/19] fixing test --- structopt-derive/src/lib.rs | 10 ++++++++-- tests/ui/struct_flatten.rs | 8 ++++---- tests/ui/struct_flatten.stderr | 8 ++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index fc8d6388..664cb73c 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -468,7 +468,10 @@ fn gen_augment_clap_enum( let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app); } } - _ => todo!(), + _ => abort_call_site!( + "{}: `flatten` can only be applied on a field or on an enum variant with another enum inside", + variant.ident + ), } }, _ => { @@ -561,7 +564,10 @@ fn gen_from_subcommand( } } }, - _ => todo!(), + _ => abort_call_site!( + "{}: `flatten` can only be applied on a field or on an enum variant with another enum inside", + variant.ident + ), } }, _ => { diff --git a/tests/ui/struct_flatten.rs b/tests/ui/struct_flatten.rs index 2b205f16..768de763 100644 --- a/tests/ui/struct_flatten.rs +++ b/tests/ui/struct_flatten.rs @@ -9,10 +9,10 @@ use structopt::StructOpt; #[derive(StructOpt, Debug)] -#[structopt(name = "basic", flatten)] -struct Opt { - #[structopt(short)] - s: String, +#[structopt(name = "basic")] +enum Opt { + #[structopt(flatten)] + Variant1, } fn main() { diff --git a/tests/ui/struct_flatten.stderr b/tests/ui/struct_flatten.stderr index 7f0fc6d6..0a559c48 100644 --- a/tests/ui/struct_flatten.stderr +++ b/tests/ui/struct_flatten.stderr @@ -1,5 +1,5 @@ -error: flatten is only allowed on fields - --> $DIR/struct_flatten.rs:12:29 +error: Variant1: `flatten` can only be applied on a field or on an enum variant with another enum inside + --> $DIR/struct_flatten.rs:11:10 | -12 | #[structopt(name = "basic", flatten)] - | ^^^^^^^ +11 | #[derive(StructOpt, Debug)] + | ^^^^^^^^^ From 9eada345e40128d8023ba873d90ffbacc1eb681b Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 15:58:28 +0100 Subject: [PATCH 10/19] WIP --- structopt-derive/src/lib.rs | 10 ++-------- tests/ui/struct_flatten.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 664cb73c..9f635b0e 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -468,10 +468,7 @@ fn gen_augment_clap_enum( let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app); } } - _ => abort_call_site!( - "{}: `flatten` can only be applied on a field or on an enum variant with another enum inside", - variant.ident - ), + _ => abort!(variant.span(), "`flatten` is usable only with single-typed tuple variants"), } }, _ => { @@ -564,10 +561,7 @@ fn gen_from_subcommand( } } }, - _ => abort_call_site!( - "{}: `flatten` can only be applied on a field or on an enum variant with another enum inside", - variant.ident - ), + _ => abort!(variant.span(), "`flatten` is usable only with single-typed tuple variants"), } }, _ => { diff --git a/tests/ui/struct_flatten.stderr b/tests/ui/struct_flatten.stderr index 0a559c48..9b69b5f8 100644 --- a/tests/ui/struct_flatten.stderr +++ b/tests/ui/struct_flatten.stderr @@ -1,5 +1,5 @@ -error: Variant1: `flatten` can only be applied on a field or on an enum variant with another enum inside - --> $DIR/struct_flatten.rs:11:10 +error: `flatten` is usable only with single-typed tuple variants + --> $DIR/struct_flatten.rs:14:5 | -11 | #[derive(StructOpt, Debug)] - | ^^^^^^^^^ +14 | #[structopt(flatten)] + | ^ From d543dab7b08598b9aad02efa470e250338ac2ab0 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 16:07:03 +0100 Subject: [PATCH 11/19] Revert "WIP" This reverts commit b678b051ca21ee7e07cac46061b0b66fd7757290. --- structopt-derive/src/lib.rs | 91 +++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 9f635b0e..38888c6b 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -539,49 +539,57 @@ fn gen_from_subcommand( ) -> TokenStream { use syn::Fields::*; - let matches = variants.iter().map(|variant| { - let attrs = Attrs::from_struct( - variant.span(), - &variant.attrs, - Name::Derived(variant.ident.clone()), - Some(parent_attribute), - parent_attribute.casing(), - parent_attribute.env_casing(), - ); - let kind = attrs.kind(); + let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants.iter() + .map(|variant| { + let attrs = Attrs::from_struct( + variant.span(), + &variant.attrs, + Name::Derived(variant.ident.clone()), + Some(parent_attribute), + parent_attribute.casing(), + parent_attribute.env_casing(), + ); + (variant, attrs) + }) + .partition(|(_, attrs)| { + let kind = attrs.kind(); + match &*kind { + Kind::Flatten => true, + _ => false + } + }); + + let match_arms = variants.iter().map(|(variant, attrs)| { + let sub_name = attrs.cased_name(); let variant_name = &variant.ident; - match &*kind { - Kind::Flatten => { - match variant.fields { - Unnamed(ref fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed[0]; - quote! { - if let Some(res) = <#ty as ::structopt::StructOptInternal>::from_subcommand(sub) { - return Some(#name :: #variant_name (res)); - } - } - }, - _ => abort!(variant.span(), "`flatten` is usable only with single-typed tuple variants"), - } - }, - _ => { - let sub_name = attrs.cased_name(); - let constructor_block = match variant.fields { - Named(ref fields) => gen_constructor(&fields.named, &attrs), - Unit => quote!(), - Unnamed(ref fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed[0]; - quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) - } - Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), - }; + let constructor_block = match variant.fields { + Named(ref fields) => gen_constructor(&fields.named, &attrs), + Unit => quote!(), + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; + quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) ) + } + Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), + }; + quote! { + (#sub_name, Some(matches)) => { + Some(#name :: #variant_name #constructor_block) + } + } + }); + let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| { + let variant_name = &variant.ident; + match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0]; quote! { - if let (#sub_name, Some(matches)) = sub { - return Some(#name :: #variant_name #constructor_block); + if let Some(res) = <#ty as ::structopt::StructOptInternal>::from_subcommand(other) { + return Some(#name :: #variant_name (res)); } } }, + _ => abort!(variant.span(), "`flatten` is usable only with single-typed tuple variants"), } }); @@ -589,8 +597,13 @@ fn gen_from_subcommand( fn from_subcommand<'a, 'b>( sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>) ) -> Option { - #( #matches )*; - None + match sub { + #( #match_arms ),*, + other => { + #( #child_subcommands )*; + None + } + } } } } From 40abf6d3c2046177a2da14aed0cb67910d4e264c Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 16:15:26 +0100 Subject: [PATCH 12/19] WIP --- structopt-derive/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 38888c6b..7186dfb3 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -516,9 +516,9 @@ fn gen_augment_clap_enum( fn augment_clap<'a, 'b>( app: ::structopt::clap::App<'a, 'b> ) -> ::structopt::clap::App<'a, 'b> { - let app = app #app_methods #version; + let app = app #app_methods; #( #subcommands )*; - app + app #version } } } From 47346b02b99ca3a38a967289c01027a825e7b86d Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 16:21:54 +0100 Subject: [PATCH 13/19] WIP --- tests/ui/{struct_flatten.rs => enum_flatten.rs} | 0 tests/ui/{struct_flatten.stderr => enum_flatten.stderr} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{struct_flatten.rs => enum_flatten.rs} (100%) rename tests/ui/{struct_flatten.stderr => enum_flatten.stderr} (100%) diff --git a/tests/ui/struct_flatten.rs b/tests/ui/enum_flatten.rs similarity index 100% rename from tests/ui/struct_flatten.rs rename to tests/ui/enum_flatten.rs diff --git a/tests/ui/struct_flatten.stderr b/tests/ui/enum_flatten.stderr similarity index 100% rename from tests/ui/struct_flatten.stderr rename to tests/ui/enum_flatten.stderr From fa66d71f05518d4725d10f578894e937bcc80f30 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 16:44:30 +0100 Subject: [PATCH 14/19] WIP --- src/lib.rs | 36 +++++++++++++++++++++++++++++++++++- tests/ui/enum_flatten.stderr | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 33a49882..b52260e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,7 +279,7 @@ //! //! - [`flatten`](#flattening): `flatten` //! -//! Usable only on field-level. +//! Usable on field-level or single-typed tuple variants. //! //! - [`subcommand`](#subcommands): `subcommand` //! @@ -867,6 +867,40 @@ //! } //! ``` //! +//! ### Flattening enums of subcommands +//! +//! It is also possible to combine multiple enums of subcommands into one. +//! All the subcommands will be on the same level. +//! +//! ``` +//! # use structopt::StructOpt; +//! # fn main() {} +//! #[derive(StructOpt)] +//! enum BaseCli { +//! Command1(Command1), +//! } +//! +//! #[derive(StructOpt)] +//! struct Command1 { +//! arg1: i32, +//! } +//! +//! #[derive(StructOpt)] +//! struct Command2 { +//! arg2: i32, +//! } +//! +//! #[derive(StructOpt)] +//! enum Opt { +//! #[structopt(flatten)] +//! BaseCli(BaseCli), +//! Command2(Command2), +//! } +//! ``` +//! +//! + `cli command1 42` +//! + `cli command2 42` +//! //! ## Flattening //! //! It can sometimes be useful to group related arguments in a substruct, diff --git a/tests/ui/enum_flatten.stderr b/tests/ui/enum_flatten.stderr index 9b69b5f8..5307cee7 100644 --- a/tests/ui/enum_flatten.stderr +++ b/tests/ui/enum_flatten.stderr @@ -1,5 +1,5 @@ error: `flatten` is usable only with single-typed tuple variants - --> $DIR/struct_flatten.rs:14:5 + --> $DIR/enum_flatten.rs:14:5 | 14 | #[structopt(flatten)] | ^ From 4322e469e3883130317bbf1511e2946a1686da90 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 16:58:49 +0100 Subject: [PATCH 15/19] Update src/lib.rs Co-Authored-By: CreepySkeleton --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b52260e3..caf7c935 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -867,7 +867,7 @@ //! } //! ``` //! -//! ### Flattening enums of subcommands +//! ### Flattening subcommands //! //! It is also possible to combine multiple enums of subcommands into one. //! All the subcommands will be on the same level. From ede94a2eda6c592666002515cfbeb605bac05b76 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 16:59:22 +0100 Subject: [PATCH 16/19] Update structopt-derive/src/lib.rs Co-Authored-By: CreepySkeleton --- structopt-derive/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 7186dfb3..983d780f 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -493,7 +493,7 @@ fn gen_augment_clap_enum( } } } - Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), + Unnamed(..) => abort!(variant.span(), "non single-typed tuple enums are not supported"), }; let name = attrs.cased_name(); From d67f4f77e170a45ef92705f79b5c7ba3e2a2d2c8 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 17:01:34 +0100 Subject: [PATCH 17/19] WIP --- src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index caf7c935..f7cb9faa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ //! - [Skipping fields](#skipping-fields) //! - [Subcommands](#subcommands) //! - [Optional subcommands](#optional-subcommands) +//! - [Flattening subcommands](#flattening-subcommands) //! - [Flattening](#flattening) //! - [Custom string parsers](#custom-string-parsers) //! @@ -898,8 +899,10 @@ //! } //! ``` //! -//! + `cli command1 42` -//! + `cli command2 42` +//! ```shell +//! cli command1 42 +//! cli command2 42 +//! ``` //! //! ## Flattening //! From 87d9d7c5f714b653019961573544bf3047d05a65 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 17:09:41 +0100 Subject: [PATCH 18/19] cargo fmt --- structopt-derive/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 983d780f..f3704b04 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -539,7 +539,8 @@ fn gen_from_subcommand( ) -> TokenStream { use syn::Fields::*; - let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants.iter() + let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants + .iter() .map(|variant| { let attrs = Attrs::from_struct( variant.span(), @@ -555,7 +556,7 @@ fn gen_from_subcommand( let kind = attrs.kind(); match &*kind { Kind::Flatten => true, - _ => false + _ => false, } }); From 5cb70bf595b7fbe3cf316632987954a634bbcd2c Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 16 Jan 2020 17:15:43 +0100 Subject: [PATCH 19/19] WIP --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7cb9faa..eecb1568 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -878,16 +878,16 @@ //! # fn main() {} //! #[derive(StructOpt)] //! enum BaseCli { -//! Command1(Command1), +//! Ghost10(Ghost10), //! } //! //! #[derive(StructOpt)] -//! struct Command1 { +//! struct Ghost10 { //! arg1: i32, //! } //! //! #[derive(StructOpt)] -//! struct Command2 { +//! struct Dex { //! arg2: i32, //! } //! @@ -895,13 +895,13 @@ //! enum Opt { //! #[structopt(flatten)] //! BaseCli(BaseCli), -//! Command2(Command2), +//! Dex(Dex), //! } //! ``` //! //! ```shell -//! cli command1 42 -//! cli command2 42 +//! cli ghost10 42 +//! cli dex 42 //! ``` //! //! ## Flattening