mirror of
https://github.com/clap-rs/clap
synced 2024-11-15 17:08:00 +00:00
Merge pull request #4209 from epage/prep
feat(derive): Reserve behavior needed for implicit ArgGroups
This commit is contained in:
commit
d2503a4a88
9 changed files with 69 additions and 8 deletions
|
@ -198,6 +198,7 @@ Subtle changes (i.e. compiler won't catch):
|
|||
- *(env)* Parse `--help` and `--version` like any `ArgAction::SetTrue` flag (#3776)
|
||||
- *(derive)* Leave `Arg::id` as `verbatim` casing, requiring updating of string references to other args like in `conflicts_with` or `requires` (#3282)
|
||||
- *(derive)* Doc comments for `ValueEnum` variants will now show up in `--help` (#3312)
|
||||
- *(derive)* When deriving `Args`, and `ArgGroup` is created using the type's name, reserving it for future use (#2621, #4209)
|
||||
|
||||
Easier to catch changes:
|
||||
|
||||
|
|
|
@ -34,6 +34,11 @@ impl ClapAttr {
|
|||
Some(Sp::new(AttrKind::StructOpt, attr.path.span()))
|
||||
} else if attr.path.is_ident("command") {
|
||||
Some(Sp::new(AttrKind::Command, attr.path.span()))
|
||||
} else if attr.path.is_ident("group") {
|
||||
abort!(
|
||||
attr.path.span(),
|
||||
"`#[group()]` attributes are not supported yet"
|
||||
)
|
||||
} else if attr.path.is_ident("arg") {
|
||||
Some(Sp::new(AttrKind::Arg, attr.path.span()))
|
||||
} else if attr.path.is_ident("value") {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use proc_macro_error::{abort, abort_call_site};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::ext::IdentExt;
|
||||
use syn::{
|
||||
punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field,
|
||||
Fields, Generics,
|
||||
|
@ -317,10 +318,13 @@ pub fn gen_augment(
|
|||
quote!()
|
||||
};
|
||||
let initial_app_methods = parent_item.initial_top_level_methods();
|
||||
let group_id = parent_item.ident().unraw().to_string();
|
||||
let final_app_methods = parent_item.final_top_level_methods();
|
||||
quote! {{
|
||||
#deprecations
|
||||
let #app_var = #app_var #initial_app_methods;
|
||||
let #app_var = #app_var
|
||||
#initial_app_methods
|
||||
.group(clap::ArgGroup::new(#group_id).multiple(true));
|
||||
#( #args )*
|
||||
#app_var #final_app_methods
|
||||
}}
|
||||
|
|
|
@ -36,6 +36,7 @@ pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
|
|||
#[derive(Clone)]
|
||||
pub struct Item {
|
||||
name: Name,
|
||||
ident: Ident,
|
||||
casing: Sp<CasingStyle>,
|
||||
env_casing: Sp<CasingStyle>,
|
||||
ty: Option<Type>,
|
||||
|
@ -54,13 +55,14 @@ pub struct Item {
|
|||
|
||||
impl Item {
|
||||
pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self {
|
||||
let ident = input.ident.clone();
|
||||
let span = input.ident.span();
|
||||
let attrs = &input.attrs;
|
||||
let argument_casing = Sp::new(DEFAULT_CASING, span);
|
||||
let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
|
||||
let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
|
||||
|
||||
let mut res = Self::new(name, None, argument_casing, env_casing, kind);
|
||||
let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
|
||||
let parsed_attrs = ClapAttr::parse_all(attrs);
|
||||
res.infer_kind(&parsed_attrs);
|
||||
res.push_attrs(&parsed_attrs);
|
||||
|
@ -70,13 +72,14 @@ impl Item {
|
|||
}
|
||||
|
||||
pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self {
|
||||
let ident = input.ident.clone();
|
||||
let span = input.ident.span();
|
||||
let attrs = &input.attrs;
|
||||
let argument_casing = Sp::new(DEFAULT_CASING, span);
|
||||
let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
|
||||
let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
|
||||
|
||||
let mut res = Self::new(name, None, argument_casing, env_casing, kind);
|
||||
let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
|
||||
let parsed_attrs = ClapAttr::parse_all(attrs);
|
||||
res.infer_kind(&parsed_attrs);
|
||||
res.push_attrs(&parsed_attrs);
|
||||
|
@ -86,13 +89,14 @@ impl Item {
|
|||
}
|
||||
|
||||
pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self {
|
||||
let ident = input.ident.clone();
|
||||
let span = input.ident.span();
|
||||
let attrs = &input.attrs;
|
||||
let argument_casing = Sp::new(DEFAULT_CASING, span);
|
||||
let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
|
||||
let kind = Sp::new(Kind::Value, span);
|
||||
|
||||
let mut res = Self::new(name, None, argument_casing, env_casing, kind);
|
||||
let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
|
||||
let parsed_attrs = ClapAttr::parse_all(attrs);
|
||||
res.infer_kind(&parsed_attrs);
|
||||
res.push_attrs(&parsed_attrs);
|
||||
|
@ -116,6 +120,7 @@ impl Item {
|
|||
env_casing: Sp<CasingStyle>,
|
||||
) -> Self {
|
||||
let name = variant.ident.clone();
|
||||
let ident = variant.ident.clone();
|
||||
let span = variant.span();
|
||||
let ty = match variant.fields {
|
||||
syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
|
@ -126,7 +131,14 @@ impl Item {
|
|||
}
|
||||
};
|
||||
let kind = Sp::new(Kind::Command(ty), span);
|
||||
let mut res = Self::new(Name::Derived(name), None, struct_casing, env_casing, kind);
|
||||
let mut res = Self::new(
|
||||
Name::Derived(name),
|
||||
ident,
|
||||
None,
|
||||
struct_casing,
|
||||
env_casing,
|
||||
kind,
|
||||
);
|
||||
let parsed_attrs = ClapAttr::parse_all(&variant.attrs);
|
||||
res.infer_kind(&parsed_attrs);
|
||||
res.push_attrs(&parsed_attrs);
|
||||
|
@ -161,10 +173,12 @@ impl Item {
|
|||
argument_casing: Sp<CasingStyle>,
|
||||
env_casing: Sp<CasingStyle>,
|
||||
) -> Self {
|
||||
let ident = variant.ident.clone();
|
||||
let span = variant.span();
|
||||
let kind = Sp::new(Kind::Value, span);
|
||||
let mut res = Self::new(
|
||||
Name::Derived(variant.ident.clone()),
|
||||
ident,
|
||||
None,
|
||||
argument_casing,
|
||||
env_casing,
|
||||
|
@ -186,11 +200,13 @@ impl Item {
|
|||
env_casing: Sp<CasingStyle>,
|
||||
) -> Self {
|
||||
let name = field.ident.clone().unwrap();
|
||||
let ident = field.ident.clone().unwrap();
|
||||
let span = field.span();
|
||||
let ty = Ty::from_syn_ty(&field.ty);
|
||||
let kind = Sp::new(Kind::Arg(ty), span);
|
||||
let mut res = Self::new(
|
||||
Name::Derived(name),
|
||||
ident,
|
||||
Some(field.ty.clone()),
|
||||
struct_casing,
|
||||
env_casing,
|
||||
|
@ -234,6 +250,7 @@ impl Item {
|
|||
|
||||
fn new(
|
||||
name: Name,
|
||||
ident: Ident,
|
||||
ty: Option<Type>,
|
||||
casing: Sp<CasingStyle>,
|
||||
env_casing: Sp<CasingStyle>,
|
||||
|
@ -241,6 +258,7 @@ impl Item {
|
|||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
ident,
|
||||
ty,
|
||||
casing,
|
||||
env_casing,
|
||||
|
@ -894,6 +912,10 @@ impl Item {
|
|||
quote!( #(#next_help_heading)* )
|
||||
}
|
||||
|
||||
pub fn ident(&self) -> &Ident {
|
||||
&self.ident
|
||||
}
|
||||
|
||||
pub fn id(&self) -> TokenStream {
|
||||
self.name.clone().raw()
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub fn parser(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
/// Generates the `Subcommand` impl.
|
||||
#[proc_macro_derive(Subcommand, attributes(clap, command, arg))]
|
||||
#[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))]
|
||||
#[proc_macro_error]
|
||||
pub fn subcommand(input: TokenStream) -> TokenStream {
|
||||
let input: DeriveInput = parse_macro_input!(input);
|
||||
|
@ -58,7 +58,7 @@ pub fn subcommand(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
/// Generates the `Args` impl.
|
||||
#[proc_macro_derive(Args, attributes(clap, command, arg))]
|
||||
#[proc_macro_derive(Args, attributes(clap, command, arg, group))]
|
||||
#[proc_macro_error]
|
||||
pub fn args(input: TokenStream) -> TokenStream {
|
||||
let input: DeriveInput = parse_macro_input!(input);
|
||||
|
|
|
@ -282,6 +282,10 @@ pub trait FromArgMatches: Sized {
|
|||
/// }
|
||||
/// ```
|
||||
pub trait Args: FromArgMatches + Sized {
|
||||
/// Report the [`ArgGroup::id`][crate::ArgGroup::id] for this set of arguments
|
||||
fn group_id() -> Option<crate::Id> {
|
||||
None
|
||||
}
|
||||
/// Append to [`Command`] so it can instantiate `Self`.
|
||||
///
|
||||
/// See also [`CommandFactory`].
|
||||
|
|
23
tests/derive/groups.rs
Normal file
23
tests/derive/groups.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[test]
|
||||
fn test_safely_nest_parser() {
|
||||
#[derive(Parser, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[command(flatten)]
|
||||
foo: Foo,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, PartialEq)]
|
||||
struct Foo {
|
||||
#[arg(long)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
foo: Foo { foo: true }
|
||||
},
|
||||
Opt::try_parse_from(&["test", "--foo"]).unwrap()
|
||||
);
|
||||
}
|
|
@ -13,6 +13,7 @@ mod explicit_name_no_renaming;
|
|||
mod flags;
|
||||
mod flatten;
|
||||
mod generic;
|
||||
mod groups;
|
||||
mod help;
|
||||
mod issues;
|
||||
mod macros;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use clap::Args;
|
||||
use clap::Parser;
|
||||
|
||||
#[test]
|
||||
|
@ -251,7 +252,7 @@ fn test_rename_all_is_not_propagated_from_struct_into_flattened() {
|
|||
foo: Foo,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, PartialEq)]
|
||||
#[derive(Args, Debug, PartialEq)]
|
||||
struct Foo {
|
||||
#[arg(long)]
|
||||
foo: bool,
|
||||
|
|
Loading…
Reference in a new issue