feat(derive): Add skip attribute support for enum variants

> Resolves #493

This is a port of https://github.com/TeXitoi/structopt/pull/494

This is part of #2809
This commit is contained in:
Ed Page 2021-10-06 13:52:02 -05:00
parent 5512c90380
commit 16023cf157
3 changed files with 62 additions and 12 deletions

View file

@ -552,9 +552,7 @@ impl Attrs {
res.kind = Sp::new(Kind::Subcommand(ty), res.kind.span()); res.kind = Sp::new(Kind::Subcommand(ty), res.kind.span());
} }
Kind::Skip(_) => { Kind::Skip(_) => (),
abort!(res.kind.span(), "skip is not supported on variants");
}
Kind::FromGlobal(_) => { Kind::FromGlobal(_) => {
abort!(res.kind.span(), "from_global is not supported on variants"); abort!(res.kind.span(), "from_global is not supported on variants");
} }

View file

@ -121,7 +121,7 @@ fn gen_augment(
let subcommands: Vec<_> = variants let subcommands: Vec<_> = variants
.iter() .iter()
.map(|variant| { .filter_map(|variant| {
let attrs = Attrs::from_variant( let attrs = Attrs::from_variant(
variant, variant,
parent_attribute.casing(), parent_attribute.casing(),
@ -130,6 +130,8 @@ fn gen_augment(
let kind = attrs.kind(); let kind = attrs.kind();
match &*kind { match &*kind {
Kind::Skip(_) => None,
Kind::ExternalSubcommand => { Kind::ExternalSubcommand => {
let ty = match variant.fields { let ty = match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
@ -141,7 +143,7 @@ fn gen_augment(
or `Vec<OsString>`." or `Vec<OsString>`."
), ),
}; };
match subty_if_name(ty, "Vec") { let subcommand = match subty_if_name(ty, "Vec") {
Some(subty) => { Some(subty) => {
if is_simple_ty(subty, "OsString") { if is_simple_ty(subty, "OsString") {
quote_spanned! { kind.span()=> quote_spanned! { kind.span()=>
@ -159,13 +161,14 @@ fn gen_augment(
"The type must be `Vec<_>` \ "The type must be `Vec<_>` \
to be used with `external_subcommand`." to be used with `external_subcommand`."
), ),
} };
Some(subcommand)
} }
Kind::Flatten => match variant.fields { Kind::Flatten => match variant.fields {
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
let ty = &unnamed[0]; let ty = &unnamed[0];
if override_required { let subcommand = if override_required {
quote! { quote! {
let app = <#ty as clap::Subcommand>::augment_subcommands_for_update(app); let app = <#ty as clap::Subcommand>::augment_subcommands_for_update(app);
} }
@ -173,7 +176,8 @@ fn gen_augment(
quote! { quote! {
let app = <#ty as clap::Subcommand>::augment_subcommands(app); let app = <#ty as clap::Subcommand>::augment_subcommands(app);
} }
} };
Some(subcommand)
} }
_ => abort!( _ => abort!(
variant, variant,
@ -212,14 +216,15 @@ fn gen_augment(
let name = attrs.cased_name(); let name = attrs.cased_name();
let from_attrs = attrs.top_level_methods(); let from_attrs = attrs.top_level_methods();
let version = attrs.version(); let version = attrs.version();
quote! { let subcommand = quote! {
let app = app.subcommand({ let app = app.subcommand({
let #app_var = clap::App::new(#name); let #app_var = clap::App::new(#name);
let #app_var = #arg_block; let #app_var = #arg_block;
let #app_var = #app_var.setting(::clap::AppSettings::SubcommandRequiredElseHelp); let #app_var = #app_var.setting(::clap::AppSettings::SubcommandRequiredElseHelp);
#app_var#from_attrs#version #app_var#from_attrs#version
}); });
} };
Some(subcommand)
} }
_ => { _ => {
@ -253,13 +258,14 @@ fn gen_augment(
let name = attrs.cased_name(); let name = attrs.cased_name();
let from_attrs = attrs.top_level_methods(); let from_attrs = attrs.top_level_methods();
let version = attrs.version(); let version = attrs.version();
quote! { let subcommand = quote! {
let app = app.subcommand({ let app = app.subcommand({
let #app_var = clap::App::new(#name); let #app_var = clap::App::new(#name);
let #app_var = #arg_block; let #app_var = #arg_block;
#app_var#from_attrs#version #app_var#from_attrs#version
}); });
} };
Some(subcommand)
} }
} }
}) })

View file

@ -474,3 +474,49 @@ fn subcommand_name_not_literal() {
assert!(Opt::try_parse_from(&["test", "renamed"]).is_ok()); assert!(Opt::try_parse_from(&["test", "renamed"]).is_ok());
} }
#[test]
fn skip_subcommand() {
#[derive(Debug, PartialEq, Clap)]
struct Opt {
#[clap(subcommand)]
sub: Subcommands,
}
#[derive(Debug, PartialEq, Clap)]
enum Subcommands {
Add,
Remove,
#[allow(dead_code)]
#[clap(skip)]
Skip,
}
assert_eq!(
Opt::parse_from(&["test", "add"]),
Opt {
sub: Subcommands::Add
}
);
assert_eq!(
Opt::parse_from(&["test", "remove"]),
Opt {
sub: Subcommands::Remove
}
);
let res = Opt::try_parse_from(&["test", "skip"]);
assert!(
matches!(
res,
Err(clap::Error {
kind: clap::ErrorKind::UnknownArgument,
..
})
),
"Unexpected result: {:?}",
res
);
}