mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +00:00
perf(derive): Consolidate field/variant parsing
This commit is contained in:
parent
6e8b366107
commit
5264a62181
4 changed files with 127 additions and 121 deletions
|
@ -36,8 +36,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
|
|||
}) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let fields = &fields.named;
|
||||
gen_for_struct(&item, ident, &input.generics, fields)
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Unit,
|
||||
|
@ -45,8 +52,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
|
|||
}) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let fields = &Punctuated::<Field, Comma>::new();
|
||||
gen_for_struct(&item, ident, &input.generics, fields)
|
||||
let fields = Punctuated::<Field, Comma>::new();
|
||||
let fields = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
|
||||
}
|
||||
|
@ -56,12 +70,12 @@ pub fn gen_for_struct(
|
|||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
fields: &Punctuated<Field, Comma>,
|
||||
fields: &[(&Field, Item)],
|
||||
) -> TokenStream {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let constructor = gen_constructor(fields, item);
|
||||
let updater = gen_updater(fields, item, true);
|
||||
let constructor = gen_constructor(fields);
|
||||
let updater = gen_updater(fields, true);
|
||||
let raw_deprecated = raw_deprecated();
|
||||
|
||||
let app_var = Ident::new("__clap_app", Span::call_site());
|
||||
|
@ -131,17 +145,12 @@ pub fn gen_for_struct(
|
|||
/// Generate a block of code to add arguments/subcommands corresponding to
|
||||
/// the `fields` to an cmd.
|
||||
pub fn gen_augment(
|
||||
fields: &Punctuated<Field, Comma>,
|
||||
fields: &[(&Field, Item)],
|
||||
app_var: &Ident,
|
||||
parent_item: &Item,
|
||||
override_required: bool,
|
||||
) -> TokenStream {
|
||||
let mut subcmds = fields.iter().filter_map(|field| {
|
||||
let item = Item::from_args_field(
|
||||
field,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
let mut subcmds = fields.iter().filter_map(|(field, item)| {
|
||||
let kind = item.kind();
|
||||
if let Kind::Subcommand(ty) = &*kind {
|
||||
let subcmd_type = match (**ty, sub_type(&field.ty)) {
|
||||
|
@ -182,12 +191,7 @@ pub fn gen_augment(
|
|||
);
|
||||
}
|
||||
|
||||
let args = fields.iter().filter_map(|field| {
|
||||
let item = Item::from_args_field(
|
||||
field,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
let args = fields.iter().filter_map(|(field, item)| {
|
||||
let kind = item.kind();
|
||||
match &*kind {
|
||||
Kind::Subcommand(_)
|
||||
|
@ -313,13 +317,8 @@ pub fn gen_augment(
|
|||
}}
|
||||
}
|
||||
|
||||
pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_item: &Item) -> TokenStream {
|
||||
let fields = fields.iter().map(|field| {
|
||||
let item = Item::from_args_field(
|
||||
field,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
|
||||
let fields = fields.iter().map(|(field, item)| {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let kind = item.kind();
|
||||
let arg_matches = format_ident!("__clap_arg_matches");
|
||||
|
@ -366,7 +365,7 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_item: &Item) ->
|
|||
},
|
||||
|
||||
Kind::Arg(ty) | Kind::FromGlobal(ty) => {
|
||||
gen_parsers(&item, ty, field_name, field, None)
|
||||
gen_parsers(item, ty, field_name, field, None)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -376,17 +375,8 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_item: &Item) ->
|
|||
}}
|
||||
}
|
||||
|
||||
pub fn gen_updater(
|
||||
fields: &Punctuated<Field, Comma>,
|
||||
parent_item: &Item,
|
||||
use_self: bool,
|
||||
) -> TokenStream {
|
||||
let fields = fields.iter().map(|field| {
|
||||
let item = Item::from_args_field(
|
||||
field,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
|
||||
let fields = fields.iter().map(|(field, item)| {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let kind = item.kind();
|
||||
|
||||
|
@ -447,7 +437,7 @@ pub fn gen_updater(
|
|||
|
||||
Kind::Skip(_) => quote!(),
|
||||
|
||||
Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(&item, ty, field_name, field, Some(&access)),
|
||||
Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(item, ty, field_name, field, Some(&access)),
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ use proc_macro2::TokenStream;
|
|||
use proc_macro_error::abort_call_site;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
use syn::Token;
|
||||
use syn::Variant;
|
||||
use syn::{
|
||||
self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
|
||||
|
@ -42,8 +41,15 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
|
|||
.ok()
|
||||
.unwrap_or_default()));
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let fields = &fields.named;
|
||||
gen_for_struct(&item, ident, &input.generics, fields)
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Unit,
|
||||
|
@ -55,8 +61,15 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
|
|||
.ok()
|
||||
.unwrap_or_default()));
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let fields = &Punctuated::<Field, Comma>::new();
|
||||
gen_for_struct(&item, ident, &input.generics, fields)
|
||||
let fields = Punctuated::<Field, Comma>::new();
|
||||
let fields = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Enum(ref e) => {
|
||||
dummies::parser_enum(ident);
|
||||
|
@ -65,8 +78,16 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
|
|||
.ok()
|
||||
.unwrap_or_default()));
|
||||
let item = Item::from_subcommand_enum(input, name);
|
||||
let variants = &e.variants;
|
||||
gen_for_enum(&item, ident, &input.generics, variants)
|
||||
let variants = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let item =
|
||||
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
|
||||
(variant, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gen_for_enum(&item, ident, &input.generics, &variants)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
|
||||
}
|
||||
|
@ -76,7 +97,7 @@ fn gen_for_struct(
|
|||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
fields: &Punctuated<Field, Comma>,
|
||||
fields: &[(&Field, Item)],
|
||||
) -> TokenStream {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
|
@ -95,7 +116,7 @@ fn gen_for_enum(
|
|||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
variants: &Punctuated<Variant, Token![,]>,
|
||||
variants: &[(&Variant, Item)],
|
||||
) -> TokenStream {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
|
|
|
@ -15,10 +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::{
|
||||
punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Token,
|
||||
Variant,
|
||||
};
|
||||
use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
|
||||
|
||||
use crate::derives::args;
|
||||
use crate::dummies;
|
||||
|
@ -34,8 +31,16 @@ pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
|
|||
Data::Enum(ref e) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_subcommand_enum(input, name);
|
||||
let variants = &e.variants;
|
||||
gen_for_enum(&item, ident, &input.generics, variants)
|
||||
let variants = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let item =
|
||||
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
|
||||
(variant, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gen_for_enum(&item, ident, &input.generics, &variants)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
|
||||
}
|
||||
|
@ -45,16 +50,16 @@ pub fn gen_for_enum(
|
|||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
variants: &Punctuated<Variant, Token![,]>,
|
||||
variants: &[(&Variant, Item)],
|
||||
) -> TokenStream {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let from_arg_matches = gen_from_arg_matches(variants, item);
|
||||
let update_from_arg_matches = gen_update_from_arg_matches(variants, item);
|
||||
let from_arg_matches = gen_from_arg_matches(variants);
|
||||
let update_from_arg_matches = gen_update_from_arg_matches(variants);
|
||||
|
||||
let augmentation = gen_augment(variants, item, false);
|
||||
let augmentation_update = gen_augment(variants, item, true);
|
||||
let has_subcommand = gen_has_subcommand(variants, item);
|
||||
let has_subcommand = gen_has_subcommand(variants);
|
||||
|
||||
quote! {
|
||||
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
|
||||
|
@ -111,7 +116,7 @@ pub fn gen_for_enum(
|
|||
}
|
||||
|
||||
fn gen_augment(
|
||||
variants: &Punctuated<Variant, Token![,]>,
|
||||
variants: &[(&Variant, Item)],
|
||||
parent_item: &Item,
|
||||
override_required: bool,
|
||||
) -> TokenStream {
|
||||
|
@ -121,12 +126,7 @@ fn gen_augment(
|
|||
|
||||
let subcommands: Vec<_> = variants
|
||||
.iter()
|
||||
.filter_map(|variant| {
|
||||
let item = Item::from_subcommand_variant(
|
||||
variant,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
.filter_map(|(variant, item)| {
|
||||
let kind = item.kind();
|
||||
|
||||
match &*kind {
|
||||
|
@ -238,7 +238,15 @@ fn gen_augment(
|
|||
let sub_augment = match variant.fields {
|
||||
Named(ref fields) => {
|
||||
// Defer to `gen_augment` for adding cmd methods
|
||||
args::gen_augment(&fields.named, &subcommand_var, &item, override_required)
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
args::gen_augment(&fields, &subcommand_var, item, override_required)
|
||||
}
|
||||
Unit => {
|
||||
let arg_block = quote!( #subcommand_var );
|
||||
|
@ -300,23 +308,14 @@ fn gen_augment(
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_has_subcommand(
|
||||
variants: &Punctuated<Variant, Token![,]>,
|
||||
parent_item: &Item,
|
||||
) -> TokenStream {
|
||||
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
|
||||
use syn::Fields::*;
|
||||
|
||||
let mut ext_subcmd = false;
|
||||
|
||||
let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
|
||||
.iter()
|
||||
.filter_map(|variant| {
|
||||
let item = Item::from_subcommand_variant(
|
||||
variant,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
|
||||
.filter_map(|(variant, item)| {
|
||||
if let Kind::ExternalSubcommand = &*item.kind() {
|
||||
ext_subcmd = true;
|
||||
None
|
||||
|
@ -367,10 +366,7 @@ fn gen_has_subcommand(
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_from_arg_matches(
|
||||
variants: &Punctuated<Variant, Token![,]>,
|
||||
parent_item: &Item,
|
||||
) -> TokenStream {
|
||||
fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
||||
use syn::Fields::*;
|
||||
|
||||
let mut ext_subcmd = None;
|
||||
|
@ -379,13 +375,7 @@ fn gen_from_arg_matches(
|
|||
let sub_arg_matches_var = format_ident!("__clap_arg_matches");
|
||||
let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
|
||||
.iter()
|
||||
.filter_map(|variant| {
|
||||
let item = Item::from_subcommand_variant(
|
||||
variant,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
|
||||
.filter_map(|(variant, item)| {
|
||||
if let Kind::ExternalSubcommand = &*item.kind() {
|
||||
if ext_subcmd.is_some() {
|
||||
abort!(
|
||||
|
@ -443,7 +433,17 @@ fn gen_from_arg_matches(
|
|||
let sub_name = item.cased_name();
|
||||
let variant_name = &variant.ident;
|
||||
let constructor_block = match variant.fields {
|
||||
Named(ref fields) => args::gen_constructor(&fields.named, item),
|
||||
Named(ref fields) => {
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
args::gen_constructor(&fields)
|
||||
},
|
||||
Unit => quote!(),
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||
let ty = &fields.unnamed[0];
|
||||
|
@ -519,21 +519,12 @@ fn gen_from_arg_matches(
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_update_from_arg_matches(
|
||||
variants: &Punctuated<Variant, Token![,]>,
|
||||
parent_item: &Item,
|
||||
) -> TokenStream {
|
||||
fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
||||
use syn::Fields::*;
|
||||
|
||||
let (flatten, variants): (Vec<_>, Vec<_>) = variants
|
||||
.iter()
|
||||
.filter_map(|variant| {
|
||||
let item = Item::from_subcommand_variant(
|
||||
variant,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
|
||||
.filter_map(|(variant, item)| {
|
||||
match &*item.kind() {
|
||||
// Fallback to `from_arg_matches_mut`
|
||||
Kind::ExternalSubcommand => None,
|
||||
|
@ -553,7 +544,15 @@ fn gen_update_from_arg_matches(
|
|||
let field_names = fields.named.iter().map(|field| {
|
||||
field.ident.as_ref().unwrap()
|
||||
}).collect::<Vec<_>>();
|
||||
let update = args::gen_updater(&fields.named, item, false);
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let update = args::gen_updater(&fields, false);
|
||||
(quote!( { #( #field_names, )* }), quote!( { #update } ))
|
||||
}
|
||||
Unit => (quote!(), quote!({})),
|
||||
|
|
|
@ -12,10 +12,7 @@ use proc_macro2::TokenStream;
|
|||
use proc_macro_error::{abort, abort_call_site};
|
||||
use quote::quote;
|
||||
use quote::quote_spanned;
|
||||
use syn::{
|
||||
punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DeriveInput, Fields, Ident,
|
||||
Variant,
|
||||
};
|
||||
use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant};
|
||||
|
||||
use crate::dummies;
|
||||
use crate::item::{Item, Kind, Name};
|
||||
|
@ -29,19 +26,23 @@ pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
|
|||
Data::Enum(ref e) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_value_enum(input, name);
|
||||
let variants = &e.variants;
|
||||
gen_for_enum(&item, ident, variants)
|
||||
let variants = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let item =
|
||||
Item::from_value_enum_variant(variant, item.casing(), item.env_casing());
|
||||
(variant, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gen_for_enum(&item, ident, &variants)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_for_enum(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
variants: &Punctuated<Variant, Comma>,
|
||||
) -> TokenStream {
|
||||
let lits = lits(variants, item);
|
||||
pub fn gen_for_enum(_item: &Item, item_name: &Ident, variants: &[(&Variant, Item)]) -> TokenStream {
|
||||
let lits = lits(variants);
|
||||
let value_variants = gen_value_variants(&lits);
|
||||
let to_possible_value = gen_to_possible_value(&lits);
|
||||
|
||||
|
@ -66,15 +67,10 @@ pub fn gen_for_enum(
|
|||
}
|
||||
}
|
||||
|
||||
fn lits(variants: &Punctuated<Variant, Comma>, parent_item: &Item) -> Vec<(TokenStream, Ident)> {
|
||||
fn lits(variants: &[(&Variant, Item)]) -> Vec<(TokenStream, Ident)> {
|
||||
variants
|
||||
.iter()
|
||||
.filter_map(|variant| {
|
||||
let item = Item::from_value_enum_variant(
|
||||
variant,
|
||||
parent_item.casing(),
|
||||
parent_item.env_casing(),
|
||||
);
|
||||
.filter_map(|(variant, item)| {
|
||||
if let Kind::Skip(_) = &*item.kind() {
|
||||
None
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue