mirror of
https://github.com/clap-rs/clap
synced 2024-12-05 02:29:12 +00:00
refactor(derive): Remove proc-macro-error dependency
Made things simpler that we were always aborting before.
This commit is contained in:
parent
cc1474f97c
commit
e030426819
15 changed files with 674 additions and 605 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -242,7 +242,6 @@ name = "clap_derive"
|
|||
version = "4.1.9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -679,30 +678,6 @@ dependencies = [
|
|||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
|
|
|
@ -33,7 +33,6 @@ syn = { version = "1.0.74", features = ["full"] }
|
|||
quote = "1.0.9"
|
||||
proc-macro2 = "1.0.42"
|
||||
heck = "0.4.0"
|
||||
proc-macro-error = "1.0.4"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use std::iter::FromIterator;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro_error::abort;
|
||||
use proc_macro_error::ResultExt;
|
||||
use quote::quote;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
|
@ -24,49 +22,44 @@ pub struct ClapAttr {
|
|||
}
|
||||
|
||||
impl ClapAttr {
|
||||
pub fn parse_all(all_attrs: &[Attribute]) -> Vec<Self> {
|
||||
all_attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let kind = if attr.path.is_ident("clap") {
|
||||
Some(Sp::new(AttrKind::Clap, attr.path.span()))
|
||||
} else if attr.path.is_ident("structopt") {
|
||||
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") {
|
||||
Some(Sp::new(AttrKind::Group, attr.path.span()))
|
||||
} else if attr.path.is_ident("arg") {
|
||||
Some(Sp::new(AttrKind::Arg, attr.path.span()))
|
||||
} else if attr.path.is_ident("value") {
|
||||
Some(Sp::new(AttrKind::Value, attr.path.span()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
kind.map(|k| (k, attr))
|
||||
})
|
||||
.flat_map(|(k, attr)| {
|
||||
attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)
|
||||
.unwrap_or_abort()
|
||||
.into_iter()
|
||||
.map(move |mut a| {
|
||||
a.kind = k;
|
||||
a
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
pub fn parse_all(all_attrs: &[Attribute]) -> Result<Vec<Self>, syn::Error> {
|
||||
let mut parsed = Vec::new();
|
||||
for attr in all_attrs {
|
||||
let kind = if attr.path.is_ident("clap") {
|
||||
Sp::new(AttrKind::Clap, attr.path.span())
|
||||
} else if attr.path.is_ident("structopt") {
|
||||
Sp::new(AttrKind::StructOpt, attr.path.span())
|
||||
} else if attr.path.is_ident("command") {
|
||||
Sp::new(AttrKind::Command, attr.path.span())
|
||||
} else if attr.path.is_ident("group") {
|
||||
Sp::new(AttrKind::Group, attr.path.span())
|
||||
} else if attr.path.is_ident("arg") {
|
||||
Sp::new(AttrKind::Arg, attr.path.span())
|
||||
} else if attr.path.is_ident("value") {
|
||||
Sp::new(AttrKind::Value, attr.path.span())
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
for mut attr in
|
||||
attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)?
|
||||
{
|
||||
attr.kind = kind;
|
||||
parsed.push(attr);
|
||||
}
|
||||
}
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
pub fn value_or_abort(&self) -> &AttrValue {
|
||||
pub fn value_or_abort(&self) -> Result<&AttrValue, syn::Error> {
|
||||
self.value
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| abort!(self.name, "attribute `{}` requires a value", self.name))
|
||||
.ok_or_else(|| format_err!(self.name, "attribute `{}` requires a value", self.name))
|
||||
}
|
||||
|
||||
pub fn lit_str_or_abort(&self) -> &LitStr {
|
||||
let value = self.value_or_abort();
|
||||
pub fn lit_str_or_abort(&self) -> Result<&LitStr, syn::Error> {
|
||||
let value = self.value_or_abort()?;
|
||||
match value {
|
||||
AttrValue::LitStr(tokens) => tokens,
|
||||
AttrValue::LitStr(tokens) => Ok(tokens),
|
||||
AttrValue::Expr(_) | AttrValue::Call(_) => {
|
||||
abort!(
|
||||
self.name,
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// MIT/Apache 2.0 license.
|
||||
|
||||
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::{
|
||||
|
@ -21,30 +20,27 @@ use syn::{
|
|||
Fields, Generics,
|
||||
};
|
||||
|
||||
use crate::dummies;
|
||||
use crate::item::{Item, Kind, Name};
|
||||
use crate::utils::{inner_type, sub_type, Sp, Ty};
|
||||
|
||||
pub fn derive_args(input: &DeriveInput) -> TokenStream {
|
||||
pub fn derive_args(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
|
||||
let ident = &input.ident;
|
||||
|
||||
dummies::args(ident);
|
||||
|
||||
match input.data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(ref fields),
|
||||
..
|
||||
}) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let item = Item::from_args_struct(input, name)?;
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Struct(DataStruct {
|
||||
|
@ -52,15 +48,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
|
|||
..
|
||||
}) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let item = Item::from_args_struct(input, name)?;
|
||||
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)
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
|
||||
|
@ -72,7 +68,7 @@ pub fn gen_for_struct(
|
|||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
fields: &[(&Field, Item)],
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
if !matches!(&*item.kind(), Kind::Command(_)) {
|
||||
abort! { item.kind().span(),
|
||||
"`{}` cannot be used with `command`",
|
||||
|
@ -82,13 +78,13 @@ pub fn gen_for_struct(
|
|||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let constructor = gen_constructor(fields);
|
||||
let updater = gen_updater(fields, 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());
|
||||
let augmentation = gen_augment(fields, &app_var, item, false);
|
||||
let augmentation_update = gen_augment(fields, &app_var, item, true);
|
||||
let augmentation = gen_augment(fields, &app_var, item, false)?;
|
||||
let augmentation_update = gen_augment(fields, &app_var, item, true)?;
|
||||
|
||||
let group_id = if item.skip_group() {
|
||||
quote!(None)
|
||||
|
@ -97,7 +93,7 @@ pub fn gen_for_struct(
|
|||
quote!(Some(clap::Id::from(#group_id)))
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
|
@ -157,7 +153,7 @@ pub fn gen_for_struct(
|
|||
#augmentation_update
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate a block of code to add arguments/subcommands corresponding to
|
||||
|
@ -167,11 +163,12 @@ pub fn gen_augment(
|
|||
app_var: &Ident,
|
||||
parent_item: &Item,
|
||||
override_required: bool,
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let mut subcommand_specified = false;
|
||||
let args = fields.iter().filter_map(|(field, item)| {
|
||||
let mut args = Vec::new();
|
||||
for (field, item) in fields {
|
||||
let kind = item.kind();
|
||||
match &*kind {
|
||||
let genned = match &*kind {
|
||||
Kind::Command(_)
|
||||
| Kind::Value
|
||||
| Kind::Skip(_, _)
|
||||
|
@ -179,7 +176,10 @@ pub fn gen_augment(
|
|||
| Kind::ExternalSubcommand => None,
|
||||
Kind::Subcommand(ty) => {
|
||||
if subcommand_specified {
|
||||
abort!(field.span(), "`#[command(subcommand)]` can only be used once per container");
|
||||
abort!(
|
||||
field.span(),
|
||||
"`#[command(subcommand)]` can only be used once per container"
|
||||
);
|
||||
}
|
||||
subcommand_specified = true;
|
||||
|
||||
|
@ -354,8 +354,9 @@ pub fn gen_augment(
|
|||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
args.push(genned);
|
||||
}
|
||||
|
||||
let deprecations = if !override_required {
|
||||
parent_item.deprecations()
|
||||
|
@ -408,7 +409,7 @@ pub fn gen_augment(
|
|||
)
|
||||
)
|
||||
};
|
||||
quote! {{
|
||||
Ok(quote! {{
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
#initial_app_methods
|
||||
|
@ -416,15 +417,15 @@ pub fn gen_augment(
|
|||
;
|
||||
#( #args )*
|
||||
#app_var #final_app_methods
|
||||
}}
|
||||
}})
|
||||
}
|
||||
|
||||
pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
|
||||
pub fn gen_constructor(fields: &[(&Field, Item)]) -> Result<TokenStream, syn::Error> {
|
||||
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");
|
||||
match &*kind {
|
||||
let genned = match &*kind {
|
||||
Kind::Command(_)
|
||||
| Kind::Value
|
||||
| Kind::ExternalSubcommand => {
|
||||
|
@ -519,18 +520,20 @@ pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
|
|||
},
|
||||
|
||||
Kind::Arg(ty) | Kind::FromGlobal(ty) => {
|
||||
gen_parsers(item, ty, field_name, field, None)
|
||||
gen_parsers(item, ty, field_name, field, None)?
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
Ok(genned)
|
||||
}).collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
|
||||
quote! {{
|
||||
Ok(quote! {{
|
||||
#( #fields ),*
|
||||
}}
|
||||
}})
|
||||
}
|
||||
|
||||
pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
|
||||
let fields = fields.iter().map(|(field, item)| {
|
||||
pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> Result<TokenStream, syn::Error> {
|
||||
let mut genned_fields = Vec::new();
|
||||
for (field, item) in fields {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let kind = item.kind();
|
||||
|
||||
|
@ -544,10 +547,8 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
|
|||
};
|
||||
let arg_matches = format_ident!("__clap_arg_matches");
|
||||
|
||||
match &*kind {
|
||||
Kind::Command(_)
|
||||
| Kind::Value
|
||||
| Kind::ExternalSubcommand => {
|
||||
let genned = match &*kind {
|
||||
Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => {
|
||||
abort! { kind.span(),
|
||||
"`{}` cannot be used with `arg`",
|
||||
kind.name(),
|
||||
|
@ -617,17 +618,20 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
|
|||
#updater
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Kind::Skip(_, _) => quote!(),
|
||||
|
||||
Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(item, ty, field_name, field, Some(&access)),
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#( #fields )*
|
||||
Kind::Arg(ty) | Kind::FromGlobal(ty) => {
|
||||
gen_parsers(item, ty, field_name, field, Some(&access))?
|
||||
}
|
||||
};
|
||||
genned_fields.push(genned);
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
#( #genned_fields )*
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_parsers(
|
||||
|
@ -636,7 +640,7 @@ fn gen_parsers(
|
|||
field_name: &Ident,
|
||||
field: &Field,
|
||||
update: Option<&TokenStream>,
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let span = ty.span();
|
||||
let convert_type = inner_type(&field.ty);
|
||||
let id = item.id();
|
||||
|
@ -709,7 +713,7 @@ fn gen_parsers(
|
|||
}
|
||||
};
|
||||
|
||||
if let Some(access) = update {
|
||||
let genned = if let Some(access) = update {
|
||||
quote_spanned! { field.span()=>
|
||||
if #arg_matches.contains_id(#id) {
|
||||
#access
|
||||
|
@ -718,7 +722,8 @@ fn gen_parsers(
|
|||
}
|
||||
} else {
|
||||
quote_spanned!(field.span()=> #field_name: #field_value )
|
||||
}
|
||||
};
|
||||
Ok(genned)
|
||||
}
|
||||
|
||||
#[cfg(feature = "raw-deprecated")]
|
||||
|
|
|
@ -18,7 +18,11 @@ use syn::{Generics, Ident};
|
|||
|
||||
use crate::item::Item;
|
||||
|
||||
pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream {
|
||||
pub fn gen_for_struct(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let name = item.cased_name();
|
||||
|
@ -51,16 +55,20 @@ pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> To
|
|||
}
|
||||
};
|
||||
|
||||
tokens
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream {
|
||||
pub fn gen_for_enum(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let name = item.cased_name();
|
||||
let app_var = Ident::new("__clap_app", Span::call_site());
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
|
@ -89,5 +97,5 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> Toke
|
|||
.arg_required_else_help(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// MIT/Apache 2.0 license.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro_error::abort_call_site;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
use syn::Variant;
|
||||
|
@ -23,11 +22,10 @@ use syn::{
|
|||
};
|
||||
|
||||
use crate::derives::{args, into_app, subcommand};
|
||||
use crate::dummies;
|
||||
use crate::item::Item;
|
||||
use crate::item::Name;
|
||||
|
||||
pub fn derive_parser(input: &DeriveInput) -> TokenStream {
|
||||
pub fn derive_parser(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
|
||||
let ident = &input.ident;
|
||||
let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
|
||||
|
||||
|
@ -36,52 +34,46 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
|
|||
fields: Fields::Named(ref fields),
|
||||
..
|
||||
}) => {
|
||||
dummies::parser_struct(ident);
|
||||
|
||||
let name = Name::Assigned(quote!(#pkg_name));
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let item = Item::from_args_struct(input, name)?;
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Unit,
|
||||
..
|
||||
}) => {
|
||||
dummies::parser_struct(ident);
|
||||
|
||||
let name = Name::Assigned(quote!(#pkg_name));
|
||||
let item = Item::from_args_struct(input, name);
|
||||
let item = Item::from_args_struct(input, name)?;
|
||||
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)
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Enum(ref e) => {
|
||||
dummies::parser_enum(ident);
|
||||
|
||||
let name = Name::Assigned(quote!(#pkg_name));
|
||||
let item = Item::from_subcommand_enum(input, name);
|
||||
let item = Item::from_subcommand_enum(input, name)?;
|
||||
let variants = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let item =
|
||||
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
|
||||
(variant, item)
|
||||
Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
|
||||
Ok((variant, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_enum(&item, ident, &input.generics, &variants)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
|
||||
|
@ -93,18 +85,18 @@ fn gen_for_struct(
|
|||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
fields: &[(&Field, Item)],
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let into_app = into_app::gen_for_struct(item, item_name, generics);
|
||||
let args = args::gen_for_struct(item, item_name, generics, fields);
|
||||
let into_app = into_app::gen_for_struct(item, item_name, generics)?;
|
||||
let args = args::gen_for_struct(item, item_name, generics, fields)?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
|
||||
|
||||
#into_app
|
||||
#args
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_for_enum(
|
||||
|
@ -112,16 +104,16 @@ fn gen_for_enum(
|
|||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
variants: &[(&Variant, Item)],
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let into_app = into_app::gen_for_enum(item, item_name, generics);
|
||||
let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants);
|
||||
let into_app = into_app::gen_for_enum(item, item_name, generics)?;
|
||||
let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
|
||||
|
||||
#into_app
|
||||
#subcommand
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,33 +13,29 @@
|
|||
// MIT/Apache 2.0 license.
|
||||
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use proc_macro_error::{abort, abort_call_site};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
|
||||
|
||||
use crate::derives::args;
|
||||
use crate::dummies;
|
||||
use crate::item::{Item, Kind, Name};
|
||||
use crate::utils::{is_simple_ty, subty_if_name};
|
||||
|
||||
pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
|
||||
pub fn derive_subcommand(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
|
||||
let ident = &input.ident;
|
||||
|
||||
dummies::subcommand(ident);
|
||||
|
||||
match input.data {
|
||||
Data::Enum(ref e) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_subcommand_enum(input, name);
|
||||
let item = Item::from_subcommand_enum(input, name)?;
|
||||
let variants = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let item =
|
||||
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
|
||||
(variant, item)
|
||||
Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
|
||||
Ok((variant, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_enum(&item, ident, &input.generics, &variants)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
|
||||
|
@ -51,7 +47,7 @@ pub fn gen_for_enum(
|
|||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
variants: &[(&Variant, Item)],
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
if !matches!(&*item.kind(), Kind::Command(_)) {
|
||||
abort! { item.kind().span(),
|
||||
"`{}` cannot be used with `command`",
|
||||
|
@ -61,14 +57,14 @@ pub fn gen_for_enum(
|
|||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let from_arg_matches = gen_from_arg_matches(variants);
|
||||
let update_from_arg_matches = gen_update_from_arg_matches(variants);
|
||||
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);
|
||||
let augmentation = gen_augment(variants, item, false)?;
|
||||
let augmentation_update = gen_augment(variants, item, true)?;
|
||||
let has_subcommand = gen_has_subcommand(variants)?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
|
@ -119,225 +115,222 @@ pub fn gen_for_enum(
|
|||
#has_subcommand
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_augment(
|
||||
variants: &[(&Variant, Item)],
|
||||
parent_item: &Item,
|
||||
override_required: bool,
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::*;
|
||||
|
||||
let app_var = Ident::new("__clap_app", Span::call_site());
|
||||
|
||||
let subcommands: Vec<_> = variants
|
||||
.iter()
|
||||
.filter_map(|(variant, item)| {
|
||||
let kind = item.kind();
|
||||
let mut subcommands = Vec::new();
|
||||
for (variant, item) in variants {
|
||||
let kind = item.kind();
|
||||
|
||||
match &*kind {
|
||||
Kind::Skip(_, _) |
|
||||
Kind::Arg(_) |
|
||||
Kind::FromGlobal(_) |
|
||||
Kind::Value => None,
|
||||
let genned = match &*kind {
|
||||
Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
|
||||
|
||||
Kind::ExternalSubcommand => {
|
||||
let ty = match variant.fields {
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
|
||||
Kind::ExternalSubcommand => {
|
||||
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_subcommand` must be \
|
||||
a single-typed tuple, and the type must be either `Vec<String>` \
|
||||
or `Vec<OsString>`."
|
||||
),
|
||||
};
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let subty = subty_if_name(ty, "Vec").unwrap_or_else(|| {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"The type must be `Vec<_>` \
|
||||
to be used with `external_subcommand`."
|
||||
)
|
||||
});
|
||||
let subcommand = quote_spanned! { kind.span()=>
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
.external_subcommand_value_parser(clap::value_parser!(#subty));
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
|
||||
Kind::Flatten(_) => match variant.fields {
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0];
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let next_help_heading = item.next_help_heading();
|
||||
let next_display_order = item.next_display_order();
|
||||
let subcommand = if override_required {
|
||||
quote! {
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
#next_help_heading
|
||||
#next_display_order;
|
||||
let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
#next_help_heading
|
||||
#next_display_order;
|
||||
let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
|
||||
}
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
_ => abort!(
|
||||
variant,
|
||||
"`flatten` is usable only with single-typed tuple variants"
|
||||
"The enum variant marked with `external_subcommand` must be \
|
||||
a single-typed tuple, and the type must be either `Vec<String>` \
|
||||
or `Vec<OsString>`."
|
||||
),
|
||||
},
|
||||
};
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let subty = subty_if_name(ty, "Vec").ok_or_else(|| {
|
||||
format_err!(
|
||||
ty.span(),
|
||||
"The type must be `Vec<_>` \
|
||||
to be used with `external_subcommand`."
|
||||
)
|
||||
})?;
|
||||
let subcommand = quote_spanned! { kind.span()=>
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
.external_subcommand_value_parser(clap::value_parser!(#subty));
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
|
||||
Kind::Subcommand(_) => {
|
||||
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
|
||||
let arg_block = match variant.fields {
|
||||
Named(_) => {
|
||||
abort!(variant, "non single-typed tuple enums are not supported")
|
||||
}
|
||||
Unit => quote!( #subcommand_var ),
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0];
|
||||
if override_required {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Unnamed(..) => {
|
||||
abort!(variant, "non single-typed tuple enums are not supported")
|
||||
}
|
||||
};
|
||||
|
||||
let name = item.cased_name();
|
||||
Kind::Flatten(_) => match variant.fields {
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0];
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let initial_app_methods = item.initial_top_level_methods();
|
||||
let final_from_attrs = item.final_top_level_methods();
|
||||
let override_methods = if override_required {
|
||||
quote_spanned! { kind.span()=>
|
||||
.subcommand_required(false)
|
||||
.arg_required_else_help(false)
|
||||
let next_help_heading = item.next_help_heading();
|
||||
let next_display_order = item.next_display_order();
|
||||
let subcommand = if override_required {
|
||||
quote! {
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
#next_help_heading
|
||||
#next_display_order;
|
||||
let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
quote! {
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
#next_help_heading
|
||||
#next_display_order;
|
||||
let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
|
||||
}
|
||||
};
|
||||
let subcommand = quote! {
|
||||
let #app_var = #app_var.subcommand({
|
||||
#deprecations;
|
||||
let #subcommand_var = clap::Command::new(#name);
|
||||
let #subcommand_var = #subcommand_var
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true);
|
||||
Some(subcommand)
|
||||
}
|
||||
_ => abort!(
|
||||
variant,
|
||||
"`flatten` is usable only with single-typed tuple variants"
|
||||
),
|
||||
},
|
||||
|
||||
Kind::Subcommand(_) => {
|
||||
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
|
||||
let arg_block = match variant.fields {
|
||||
Named(_) => {
|
||||
abort!(variant, "non single-typed tuple enums are not supported")
|
||||
}
|
||||
Unit => quote!( #subcommand_var ),
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0];
|
||||
if override_required {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Unnamed(..) => {
|
||||
abort!(variant, "non single-typed tuple enums are not supported")
|
||||
}
|
||||
};
|
||||
|
||||
let name = item.cased_name();
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let initial_app_methods = item.initial_top_level_methods();
|
||||
let final_from_attrs = item.final_top_level_methods();
|
||||
let override_methods = if override_required {
|
||||
quote_spanned! { kind.span()=>
|
||||
.subcommand_required(false)
|
||||
.arg_required_else_help(false)
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let subcommand = quote! {
|
||||
let #app_var = #app_var.subcommand({
|
||||
#deprecations;
|
||||
let #subcommand_var = clap::Command::new(#name);
|
||||
let #subcommand_var = #subcommand_var
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true);
|
||||
let #subcommand_var = #subcommand_var #initial_app_methods;
|
||||
let #subcommand_var = #arg_block;
|
||||
#subcommand_var #final_from_attrs #override_methods
|
||||
});
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
|
||||
Kind::Command(_) => {
|
||||
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
|
||||
let sub_augment = match variant.fields {
|
||||
Named(ref fields) => {
|
||||
// Defer to `gen_augment` for adding cmd methods
|
||||
let fields = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item =
|
||||
Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
args::gen_augment(&fields, &subcommand_var, item, override_required)?
|
||||
}
|
||||
Unit => {
|
||||
let arg_block = quote!( #subcommand_var );
|
||||
let initial_app_methods = item.initial_top_level_methods();
|
||||
let final_from_attrs = item.final_top_level_methods();
|
||||
quote! {
|
||||
let #subcommand_var = #subcommand_var #initial_app_methods;
|
||||
let #subcommand_var = #arg_block;
|
||||
#subcommand_var #final_from_attrs #override_methods
|
||||
});
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
|
||||
Kind::Command(_) => {
|
||||
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
|
||||
let sub_augment = match variant.fields {
|
||||
Named(ref fields) => {
|
||||
// Defer to `gen_augment` for adding cmd methods
|
||||
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)
|
||||
#subcommand_var #final_from_attrs
|
||||
}
|
||||
Unit => {
|
||||
let arg_block = quote!( #subcommand_var );
|
||||
let initial_app_methods = item.initial_top_level_methods();
|
||||
let final_from_attrs = item.final_top_level_methods();
|
||||
quote! {
|
||||
let #subcommand_var = #subcommand_var #initial_app_methods;
|
||||
let #subcommand_var = #arg_block;
|
||||
#subcommand_var #final_from_attrs
|
||||
}
|
||||
},
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0];
|
||||
let arg_block = if override_required {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Args>::augment_args_for_update(#subcommand_var)
|
||||
}
|
||||
}
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0];
|
||||
let arg_block = if override_required {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Args>::augment_args_for_update(#subcommand_var)
|
||||
}
|
||||
} else {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Args>::augment_args(#subcommand_var)
|
||||
}
|
||||
}
|
||||
};
|
||||
let initial_app_methods = item.initial_top_level_methods();
|
||||
let final_from_attrs = item.final_top_level_methods();
|
||||
quote! {
|
||||
let #subcommand_var = #subcommand_var #initial_app_methods;
|
||||
let #subcommand_var = #arg_block;
|
||||
#subcommand_var #final_from_attrs
|
||||
}
|
||||
} else {
|
||||
quote_spanned! { ty.span()=>
|
||||
{
|
||||
<#ty as clap::Args>::augment_args(#subcommand_var)
|
||||
}
|
||||
}
|
||||
};
|
||||
let initial_app_methods = item.initial_top_level_methods();
|
||||
let final_from_attrs = item.final_top_level_methods();
|
||||
quote! {
|
||||
let #subcommand_var = #subcommand_var #initial_app_methods;
|
||||
let #subcommand_var = #arg_block;
|
||||
#subcommand_var #final_from_attrs
|
||||
}
|
||||
Unnamed(..) => {
|
||||
abort!(variant, "non single-typed tuple enums are not supported")
|
||||
}
|
||||
};
|
||||
}
|
||||
Unnamed(..) => {
|
||||
abort!(variant, "non single-typed tuple enums are not supported")
|
||||
}
|
||||
};
|
||||
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let name = item.cased_name();
|
||||
let subcommand = quote! {
|
||||
let #app_var = #app_var.subcommand({
|
||||
#deprecations
|
||||
let #subcommand_var = clap::Command::new(#name);
|
||||
#sub_augment
|
||||
});
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let name = item.cased_name();
|
||||
let subcommand = quote! {
|
||||
let #app_var = #app_var.subcommand({
|
||||
#deprecations
|
||||
let #subcommand_var = clap::Command::new(#name);
|
||||
#sub_augment
|
||||
});
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
};
|
||||
subcommands.push(genned);
|
||||
}
|
||||
|
||||
let deprecations = if !override_required {
|
||||
parent_item.deprecations()
|
||||
|
@ -346,15 +339,15 @@ fn gen_augment(
|
|||
};
|
||||
let initial_app_methods = parent_item.initial_top_level_methods();
|
||||
let final_app_methods = parent_item.final_top_level_methods();
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#deprecations;
|
||||
let #app_var = #app_var #initial_app_methods;
|
||||
#( #subcommands )*;
|
||||
#app_var #final_app_methods
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
|
||||
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::*;
|
||||
|
||||
let mut ext_subcmd = false;
|
||||
|
@ -391,19 +384,20 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
.map(|(variant, _attrs)| match variant.fields {
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||
let ty = &fields.unnamed[0];
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => abort!(
|
||||
variant,
|
||||
"`flatten` is usable only with single-typed tuple variants"
|
||||
),
|
||||
});
|
||||
})
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
|
||||
if ext_subcmd {
|
||||
let genned = if ext_subcmd {
|
||||
quote! { true }
|
||||
} else {
|
||||
quote! {
|
||||
|
@ -413,77 +407,79 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(genned)
|
||||
}
|
||||
|
||||
fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
||||
fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::*;
|
||||
|
||||
let mut ext_subcmd = None;
|
||||
|
||||
let subcommand_name_var = format_ident!("__clap_name");
|
||||
let sub_arg_matches_var = format_ident!("__clap_arg_matches");
|
||||
let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
|
||||
.iter()
|
||||
.filter_map(|(variant, item)| {
|
||||
let kind = item.kind();
|
||||
match &*kind {
|
||||
Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
|
||||
|
||||
Kind::ExternalSubcommand => {
|
||||
if ext_subcmd.is_some() {
|
||||
abort!(
|
||||
item.kind().span(),
|
||||
"Only one variant can be marked with `external_subcommand`, \
|
||||
let mut ext_subcmd = None;
|
||||
let mut flatten_variants = Vec::new();
|
||||
let mut unflatten_variants = Vec::new();
|
||||
for (variant, item) in variants {
|
||||
let kind = item.kind();
|
||||
match &*kind {
|
||||
Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => {}
|
||||
|
||||
Kind::ExternalSubcommand => {
|
||||
if ext_subcmd.is_some() {
|
||||
abort!(
|
||||
item.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,
|
||||
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_subcommand` must be \
|
||||
_ => abort!(
|
||||
variant,
|
||||
"The enum variant marked with `external_subcommand` must be \
|
||||
a single-typed tuple, and the type must be either `Vec<String>` \
|
||||
or `Vec<OsString>`."
|
||||
),
|
||||
};
|
||||
),
|
||||
};
|
||||
|
||||
let (span, str_ty) = match subty_if_name(ty, "Vec") {
|
||||
Some(subty) => {
|
||||
if is_simple_ty(subty, "String") {
|
||||
(subty.span(), quote!(::std::string::String))
|
||||
} else if is_simple_ty(subty, "OsString") {
|
||||
(subty.span(), quote!(::std::ffi::OsString))
|
||||
} else {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"The type must be either `Vec<String>` or `Vec<OsString>` \
|
||||
let (span, str_ty) = match subty_if_name(ty, "Vec") {
|
||||
Some(subty) => {
|
||||
if is_simple_ty(subty, "String") {
|
||||
(subty.span(), quote!(::std::string::String))
|
||||
} else if is_simple_ty(subty, "OsString") {
|
||||
(subty.span(), quote!(::std::ffi::OsString))
|
||||
} else {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"The type must be either `Vec<String>` or `Vec<OsString>` \
|
||||
to be used with `external_subcommand`."
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
None => abort!(
|
||||
ty.span(),
|
||||
"The type must be either `Vec<String>` or `Vec<OsString>` \
|
||||
None => abort!(
|
||||
ty.span(),
|
||||
"The type must be either `Vec<String>` or `Vec<OsString>` \
|
||||
to be used with `external_subcommand`."
|
||||
),
|
||||
};
|
||||
),
|
||||
};
|
||||
|
||||
ext_subcmd = Some((span, &variant.ident, str_ty));
|
||||
None
|
||||
}
|
||||
Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
|
||||
ext_subcmd = Some((span, &variant.ident, str_ty));
|
||||
}
|
||||
})
|
||||
.partition(|(_, item)| {
|
||||
let kind = item.kind();
|
||||
matches!(&*kind, Kind::Flatten(_))
|
||||
});
|
||||
Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => {
|
||||
if matches!(&*item.kind(), Kind::Flatten(_)) {
|
||||
flatten_variants.push((variant, item));
|
||||
} else {
|
||||
unflatten_variants.push((variant, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let subcommands = variants.iter().map(|(variant, item)| {
|
||||
let subcommands = unflatten_variants.iter().map(|(variant, item)| {
|
||||
let sub_name = item.cased_name();
|
||||
let variant_name = &variant.ident;
|
||||
let constructor_block = match variant.fields {
|
||||
|
@ -492,11 +488,11 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
args::gen_constructor(&fields)
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
args::gen_constructor(&fields)?
|
||||
},
|
||||
Unit => quote!(),
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||
|
@ -506,18 +502,18 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
if #subcommand_name_var == #sub_name && !#sub_arg_matches_var.contains_id("") {
|
||||
return ::std::result::Result::Ok(Self :: #variant_name #constructor_block)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}).collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
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! {
|
||||
Ok(quote! {
|
||||
if __clap_arg_matches
|
||||
.subcommand_name()
|
||||
.map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
|
||||
|
@ -526,14 +522,14 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
|
||||
return ::std::result::Result::Ok(Self :: #variant_name (__clap_res));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => abort!(
|
||||
variant,
|
||||
"`flatten` is usable only with single-typed tuple variants"
|
||||
),
|
||||
}
|
||||
});
|
||||
}).collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
|
||||
let wildcard = match ext_subcmd {
|
||||
Some((span, var_name, str_ty)) => quote_spanned! { span=>
|
||||
|
@ -555,7 +551,7 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
};
|
||||
|
||||
let raw_deprecated = args::raw_deprecated();
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
|
||||
#raw_deprecated
|
||||
|
||||
|
@ -570,10 +566,10 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided."))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
||||
fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::*;
|
||||
|
||||
let (flatten, variants): (Vec<_>, Vec<_>) = variants
|
||||
|
@ -607,11 +603,11 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing());
|
||||
(field, item)
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let update = args::gen_updater(&fields, false);
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
let update = args::gen_updater(&fields, false)?;
|
||||
(quote!( { #( #field_names, )* }), quote!( { #update } ))
|
||||
}
|
||||
Unit => (quote!(), quote!({})),
|
||||
|
@ -630,38 +626,38 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
Self :: #variant_name #pattern if #sub_name == __clap_name => {
|
||||
let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap();
|
||||
let __clap_arg_matches = &mut __clap_arg_sub_matches;
|
||||
#updater
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let child_subcommands = flatten.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! {
|
||||
Ok(quote! {
|
||||
if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
|
||||
if let Self :: #variant_name (child) = s {
|
||||
<#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?;
|
||||
return ::std::result::Result::Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => abort!(
|
||||
variant,
|
||||
"`flatten` is usable only with single-typed tuple variants"
|
||||
),
|
||||
}
|
||||
});
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let raw_deprecated = args::raw_deprecated();
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
fn update_from_arg_matches_mut<'b>(
|
||||
&mut self,
|
||||
__clap_arg_matches: &mut clap::ArgMatches,
|
||||
|
@ -679,5 +675,5 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
|
|||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,39 +9,36 @@
|
|||
// except according to those terms.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro_error::{abort, abort_call_site};
|
||||
use quote::quote;
|
||||
use quote::quote_spanned;
|
||||
use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant};
|
||||
|
||||
use crate::dummies;
|
||||
use crate::item::{Item, Kind, Name};
|
||||
|
||||
pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
|
||||
pub fn derive_value_enum(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
|
||||
let ident = &input.ident;
|
||||
|
||||
dummies::value_enum(ident);
|
||||
|
||||
match input.data {
|
||||
Data::Enum(ref e) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
let item = Item::from_value_enum(input, name);
|
||||
let variants = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let item =
|
||||
Item::from_value_enum_variant(variant, item.casing(), item.env_casing());
|
||||
(variant, item)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let item = Item::from_value_enum(input, name)?;
|
||||
let mut variants = Vec::new();
|
||||
for variant in &e.variants {
|
||||
let item =
|
||||
Item::from_value_enum_variant(variant, item.casing(), item.env_casing())?;
|
||||
variants.push((variant, item));
|
||||
}
|
||||
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: &[(&Variant, Item)]) -> TokenStream {
|
||||
pub fn gen_for_enum(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
variants: &[(&Variant, Item)],
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
if !matches!(&*item.kind(), Kind::Value) {
|
||||
abort! { item.kind().span(),
|
||||
"`{}` cannot be used with `value`",
|
||||
|
@ -49,11 +46,11 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item)
|
|||
}
|
||||
}
|
||||
|
||||
let lits = lits(variants);
|
||||
let lits = lits(variants)?;
|
||||
let value_variants = gen_value_variants(&lits);
|
||||
let to_possible_value = gen_to_possible_value(item, &lits);
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
|
@ -71,33 +68,31 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item)
|
|||
#value_variants
|
||||
#to_possible_value
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn lits(variants: &[(&Variant, Item)]) -> Vec<(TokenStream, Ident)> {
|
||||
variants
|
||||
.iter()
|
||||
.filter_map(|(variant, item)| {
|
||||
if let Kind::Skip(_, _) = &*item.kind() {
|
||||
None
|
||||
} else {
|
||||
if !matches!(variant.fields, Fields::Unit) {
|
||||
abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
|
||||
}
|
||||
let fields = item.field_methods();
|
||||
let deprecations = item.deprecations();
|
||||
let name = item.cased_name();
|
||||
Some((
|
||||
quote_spanned! { variant.span()=> {
|
||||
#deprecations
|
||||
clap::builder::PossibleValue::new(#name)
|
||||
#fields
|
||||
}},
|
||||
variant.ident.clone(),
|
||||
))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
fn lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error> {
|
||||
let mut genned = Vec::new();
|
||||
for (variant, item) in variants {
|
||||
if let Kind::Skip(_, _) = &*item.kind() {
|
||||
continue;
|
||||
}
|
||||
if !matches!(variant.fields, Fields::Unit) {
|
||||
abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
|
||||
}
|
||||
let fields = item.field_methods();
|
||||
let deprecations = item.deprecations();
|
||||
let name = item.cased_name();
|
||||
genned.push((
|
||||
quote_spanned! { variant.span()=> {
|
||||
#deprecations
|
||||
clap::builder::PossibleValue::new(#name)
|
||||
#fields
|
||||
}},
|
||||
variant.ident.clone(),
|
||||
));
|
||||
}
|
||||
Ok(genned)
|
||||
}
|
||||
|
||||
fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
//! Dummy implementations that we emit along with an error.
|
||||
|
||||
use proc_macro2::Ident;
|
||||
use proc_macro_error::append_dummy;
|
||||
use quote::quote;
|
||||
|
||||
pub fn parser_struct(name: &Ident) {
|
||||
into_app(name);
|
||||
args(name);
|
||||
append_dummy(quote!( impl clap::Parser for #name {} ));
|
||||
#[must_use]
|
||||
pub fn parser(name: &Ident) -> proc_macro2::TokenStream {
|
||||
let into_app = into_app(name);
|
||||
quote!(
|
||||
impl clap::Parser for #name {}
|
||||
#into_app
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parser_enum(name: &Ident) {
|
||||
into_app(name);
|
||||
subcommand(name);
|
||||
append_dummy(quote!( impl clap::Parser for #name {} ));
|
||||
}
|
||||
|
||||
pub fn into_app(name: &Ident) {
|
||||
append_dummy(quote! {
|
||||
#[must_use]
|
||||
pub fn into_app(name: &Ident) -> proc_macro2::TokenStream {
|
||||
quote! {
|
||||
impl clap::CommandFactory for #name {
|
||||
fn command<'b>() -> clap::Command {
|
||||
unimplemented!()
|
||||
|
@ -26,11 +23,12 @@ pub fn into_app(name: &Ident) {
|
|||
unimplemented!()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_arg_matches(name: &Ident) {
|
||||
append_dummy(quote! {
|
||||
#[must_use]
|
||||
pub fn from_arg_matches(name: &Ident) -> proc_macro2::TokenStream {
|
||||
quote! {
|
||||
impl clap::FromArgMatches for #name {
|
||||
fn from_arg_matches(_m: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
|
||||
unimplemented!()
|
||||
|
@ -39,12 +37,13 @@ pub fn from_arg_matches(name: &Ident) {
|
|||
unimplemented!()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subcommand(name: &Ident) {
|
||||
from_arg_matches(name);
|
||||
append_dummy(quote! {
|
||||
#[must_use]
|
||||
pub fn subcommand(name: &Ident) -> proc_macro2::TokenStream {
|
||||
let from_arg_matches = from_arg_matches(name);
|
||||
quote! {
|
||||
impl clap::Subcommand for #name {
|
||||
fn augment_subcommands(_cmd: clap::Command) -> clap::Command {
|
||||
unimplemented!()
|
||||
|
@ -56,12 +55,14 @@ pub fn subcommand(name: &Ident) {
|
|||
unimplemented!()
|
||||
}
|
||||
}
|
||||
});
|
||||
#from_arg_matches
|
||||
}
|
||||
}
|
||||
|
||||
pub fn args(name: &Ident) {
|
||||
from_arg_matches(name);
|
||||
append_dummy(quote! {
|
||||
#[must_use]
|
||||
pub fn args(name: &Ident) -> proc_macro2::TokenStream {
|
||||
let from_arg_matches = from_arg_matches(name);
|
||||
quote! {
|
||||
impl clap::Args for #name {
|
||||
fn augment_args(_cmd: clap::Command) -> clap::Command {
|
||||
unimplemented!()
|
||||
|
@ -70,11 +71,13 @@ pub fn args(name: &Ident) {
|
|||
unimplemented!()
|
||||
}
|
||||
}
|
||||
});
|
||||
#from_arg_matches
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_enum(name: &Ident) {
|
||||
append_dummy(quote! {
|
||||
#[must_use]
|
||||
pub fn value_enum(name: &Ident) -> proc_macro2::TokenStream {
|
||||
quote! {
|
||||
impl clap::ValueEnum for #name {
|
||||
fn value_variants<'a>() -> &'a [Self]{
|
||||
unimplemented!()
|
||||
|
@ -86,5 +89,5 @@ pub fn value_enum(name: &Ident) {
|
|||
unimplemented!()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ use std::env;
|
|||
|
||||
use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
|
||||
use proc_macro2::{self, Span, TokenStream};
|
||||
use proc_macro_error::abort;
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use syn::DeriveInput;
|
||||
use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant};
|
||||
|
@ -53,7 +52,7 @@ pub struct Item {
|
|||
}
|
||||
|
||||
impl Item {
|
||||
pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self {
|
||||
pub fn from_args_struct(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
|
||||
let ident = input.ident.clone();
|
||||
let span = input.ident.span();
|
||||
let attrs = &input.attrs;
|
||||
|
@ -62,15 +61,15 @@ impl Item {
|
|||
let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
|
||||
|
||||
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);
|
||||
let parsed_attrs = ClapAttr::parse_all(attrs)?;
|
||||
res.infer_kind(&parsed_attrs)?;
|
||||
res.push_attrs(&parsed_attrs)?;
|
||||
res.push_doc_comment(attrs, "about", Some("long_about"));
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self {
|
||||
pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
|
||||
let ident = input.ident.clone();
|
||||
let span = input.ident.span();
|
||||
let attrs = &input.attrs;
|
||||
|
@ -79,15 +78,15 @@ impl Item {
|
|||
let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
|
||||
|
||||
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);
|
||||
let parsed_attrs = ClapAttr::parse_all(attrs)?;
|
||||
res.infer_kind(&parsed_attrs)?;
|
||||
res.push_attrs(&parsed_attrs)?;
|
||||
res.push_doc_comment(attrs, "about", Some("long_about"));
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self {
|
||||
pub fn from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
|
||||
let ident = input.ident.clone();
|
||||
let span = input.ident.span();
|
||||
let attrs = &input.attrs;
|
||||
|
@ -96,9 +95,9 @@ impl Item {
|
|||
let kind = Sp::new(Kind::Value, span);
|
||||
|
||||
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);
|
||||
let parsed_attrs = ClapAttr::parse_all(attrs)?;
|
||||
res.infer_kind(&parsed_attrs)?;
|
||||
res.push_attrs(&parsed_attrs)?;
|
||||
// Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation
|
||||
// to
|
||||
|
||||
|
@ -110,14 +109,14 @@ impl Item {
|
|||
);
|
||||
}
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn from_subcommand_variant(
|
||||
variant: &Variant,
|
||||
struct_casing: Sp<CasingStyle>,
|
||||
env_casing: Sp<CasingStyle>,
|
||||
) -> Self {
|
||||
) -> Result<Self, syn::Error> {
|
||||
let name = variant.ident.clone();
|
||||
let ident = variant.ident.clone();
|
||||
let span = variant.span();
|
||||
|
@ -138,9 +137,9 @@ impl Item {
|
|||
env_casing,
|
||||
kind,
|
||||
);
|
||||
let parsed_attrs = ClapAttr::parse_all(&variant.attrs);
|
||||
res.infer_kind(&parsed_attrs);
|
||||
res.push_attrs(&parsed_attrs);
|
||||
let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
|
||||
res.infer_kind(&parsed_attrs)?;
|
||||
res.push_attrs(&parsed_attrs)?;
|
||||
if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) {
|
||||
res.push_doc_comment(&variant.attrs, "about", Some("long_about"));
|
||||
}
|
||||
|
@ -164,14 +163,14 @@ impl Item {
|
|||
| Kind::Arg(_) => (),
|
||||
}
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn from_value_enum_variant(
|
||||
variant: &Variant,
|
||||
argument_casing: Sp<CasingStyle>,
|
||||
env_casing: Sp<CasingStyle>,
|
||||
) -> Self {
|
||||
) -> Result<Self, syn::Error> {
|
||||
let ident = variant.ident.clone();
|
||||
let span = variant.span();
|
||||
let kind = Sp::new(Kind::Value, span);
|
||||
|
@ -183,21 +182,21 @@ impl Item {
|
|||
env_casing,
|
||||
kind,
|
||||
);
|
||||
let parsed_attrs = ClapAttr::parse_all(&variant.attrs);
|
||||
res.infer_kind(&parsed_attrs);
|
||||
res.push_attrs(&parsed_attrs);
|
||||
let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
|
||||
res.infer_kind(&parsed_attrs)?;
|
||||
res.push_attrs(&parsed_attrs)?;
|
||||
if matches!(&*res.kind, Kind::Value) {
|
||||
res.push_doc_comment(&variant.attrs, "help", None);
|
||||
}
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn from_args_field(
|
||||
field: &Field,
|
||||
struct_casing: Sp<CasingStyle>,
|
||||
env_casing: Sp<CasingStyle>,
|
||||
) -> Self {
|
||||
) -> Result<Self, syn::Error> {
|
||||
let name = field.ident.clone().unwrap();
|
||||
let ident = field.ident.clone().unwrap();
|
||||
let span = field.span();
|
||||
|
@ -211,9 +210,9 @@ impl Item {
|
|||
env_casing,
|
||||
kind,
|
||||
);
|
||||
let parsed_attrs = ClapAttr::parse_all(&field.attrs);
|
||||
res.infer_kind(&parsed_attrs);
|
||||
res.push_attrs(&parsed_attrs);
|
||||
let parsed_attrs = ClapAttr::parse_all(&field.attrs)?;
|
||||
res.infer_kind(&parsed_attrs)?;
|
||||
res.push_attrs(&parsed_attrs)?;
|
||||
if matches!(&*res.kind, Kind::Arg(_)) {
|
||||
res.push_doc_comment(&field.attrs, "help", Some("long_help"));
|
||||
}
|
||||
|
@ -244,7 +243,7 @@ impl Item {
|
|||
| Kind::ExternalSubcommand => {}
|
||||
}
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn new(
|
||||
|
@ -329,7 +328,7 @@ impl Item {
|
|||
}
|
||||
}
|
||||
|
||||
fn infer_kind(&mut self, attrs: &[ClapAttr]) {
|
||||
fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
|
||||
for attr in attrs {
|
||||
if let Some(AttrValue::Call(_)) = &attr.value {
|
||||
continue;
|
||||
|
@ -339,7 +338,7 @@ impl Item {
|
|||
let kind = match &attr.magic {
|
||||
Some(MagicAttrName::FromGlobal) => {
|
||||
if attr.value.is_some() {
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
abort!(expr, "attribute `{}` does not accept a value", attr.name);
|
||||
}
|
||||
let ty = self
|
||||
|
@ -352,7 +351,7 @@ impl Item {
|
|||
}
|
||||
Some(MagicAttrName::Subcommand) if attr.value.is_none() => {
|
||||
if attr.value.is_some() {
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
abort!(expr, "attribute `{}` does not accept a value", attr.name);
|
||||
}
|
||||
let ty = self
|
||||
|
@ -365,7 +364,7 @@ impl Item {
|
|||
}
|
||||
Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => {
|
||||
if attr.value.is_some() {
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
abort!(expr, "attribute `{}` does not accept a value", attr.name);
|
||||
}
|
||||
let kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span());
|
||||
|
@ -373,7 +372,7 @@ impl Item {
|
|||
}
|
||||
Some(MagicAttrName::Flatten) if attr.value.is_none() => {
|
||||
if attr.value.is_some() {
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
abort!(expr, "attribute `{}` does not accept a value", attr.name);
|
||||
}
|
||||
let ty = self
|
||||
|
@ -396,12 +395,14 @@ impl Item {
|
|||
};
|
||||
|
||||
if let Some(kind) = kind {
|
||||
self.set_kind(kind);
|
||||
self.set_kind(kind)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_attrs(&mut self, attrs: &[ClapAttr]) {
|
||||
fn push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
|
||||
for attr in attrs {
|
||||
let actual_attr_kind = *attr.kind.get();
|
||||
let expected_attr_kind = self.kind.attr_kind();
|
||||
|
@ -437,7 +438,7 @@ impl Item {
|
|||
|
||||
match &attr.magic {
|
||||
Some(MagicAttrName::Short) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
self.push_method(
|
||||
*attr.kind.get(),
|
||||
|
@ -447,13 +448,13 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::Long) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing));
|
||||
}
|
||||
|
||||
Some(MagicAttrName::ValueParser) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
self.deprecations.push(Deprecation {
|
||||
span: attr.name.span(),
|
||||
|
@ -465,7 +466,7 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::Action) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
self.deprecations.push(Deprecation {
|
||||
span: attr.name.span(),
|
||||
|
@ -477,7 +478,7 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::Env) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
self.push_method(
|
||||
*attr.kind.get(),
|
||||
|
@ -487,7 +488,7 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::ValueEnum) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
self.is_enum = true
|
||||
}
|
||||
|
@ -497,45 +498,45 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::About) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Command]);
|
||||
assert_attr_kind(attr, &[AttrKind::Command])?;
|
||||
|
||||
if let Some(method) =
|
||||
Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")
|
||||
Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")?
|
||||
{
|
||||
self.methods.push(method);
|
||||
}
|
||||
}
|
||||
|
||||
Some(MagicAttrName::LongAbout) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Command]);
|
||||
assert_attr_kind(attr, &[AttrKind::Command])?;
|
||||
|
||||
self.force_long_help = true;
|
||||
}
|
||||
|
||||
Some(MagicAttrName::LongHelp) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
self.force_long_help = true;
|
||||
}
|
||||
|
||||
Some(MagicAttrName::Author) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Command]);
|
||||
assert_attr_kind(attr, &[AttrKind::Command])?;
|
||||
|
||||
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS") {
|
||||
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS")? {
|
||||
self.methods.push(method);
|
||||
}
|
||||
}
|
||||
|
||||
Some(MagicAttrName::Version) if attr.value.is_none() => {
|
||||
assert_attr_kind(attr, &[AttrKind::Command]);
|
||||
assert_attr_kind(attr, &[AttrKind::Command])?;
|
||||
|
||||
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION") {
|
||||
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION")? {
|
||||
self.methods.push(method);
|
||||
}
|
||||
}
|
||||
|
||||
Some(MagicAttrName::DefaultValueT) => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
let ty = if let Some(ty) = self.ty.as_ref() {
|
||||
ty
|
||||
|
@ -543,7 +544,7 @@ impl Item {
|
|||
abort!(
|
||||
attr.name.clone(),
|
||||
"#[arg(default_value_t)] (without an argument) can be used \
|
||||
only on field level";
|
||||
only on field level\n\n= note: {note}\n\n",
|
||||
|
||||
note = "see \
|
||||
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
|
||||
|
@ -583,7 +584,7 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::DefaultValuesT) => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
let ty = if let Some(ty) = self.ty.as_ref() {
|
||||
ty
|
||||
|
@ -591,18 +592,18 @@ impl Item {
|
|||
abort!(
|
||||
attr.name.clone(),
|
||||
"#[arg(default_values_t)] (without an argument) can be used \
|
||||
only on field level";
|
||||
only on field level\n\n= note: {note}\n\n",
|
||||
|
||||
note = "see \
|
||||
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
|
||||
};
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
|
||||
let container_type = Ty::from_syn_ty(ty);
|
||||
if *container_type != Ty::Vec {
|
||||
abort!(
|
||||
attr.name.clone(),
|
||||
"#[arg(default_values_t)] can be used only on Vec types";
|
||||
"#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n",
|
||||
|
||||
note = "see \
|
||||
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
|
||||
|
@ -667,7 +668,7 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::DefaultValueOsT) => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
let ty = if let Some(ty) = self.ty.as_ref() {
|
||||
ty
|
||||
|
@ -675,7 +676,7 @@ impl Item {
|
|||
abort!(
|
||||
attr.name.clone(),
|
||||
"#[arg(default_value_os_t)] (without an argument) can be used \
|
||||
only on field level";
|
||||
only on field level\n\n= note: {note}\n\n",
|
||||
|
||||
note = "see \
|
||||
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
|
||||
|
@ -715,7 +716,7 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::DefaultValuesOsT) => {
|
||||
assert_attr_kind(attr, &[AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Arg])?;
|
||||
|
||||
let ty = if let Some(ty) = self.ty.as_ref() {
|
||||
ty
|
||||
|
@ -723,18 +724,18 @@ impl Item {
|
|||
abort!(
|
||||
attr.name.clone(),
|
||||
"#[arg(default_values_os_t)] (without an argument) can be used \
|
||||
only on field level";
|
||||
only on field level\n\n= note: {note}\n\n",
|
||||
|
||||
note = "see \
|
||||
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
|
||||
};
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
|
||||
let container_type = Ty::from_syn_ty(ty);
|
||||
if *container_type != Ty::Vec {
|
||||
abort!(
|
||||
attr.name.clone(),
|
||||
"#[arg(default_values_os_t)] can be used only on Vec types";
|
||||
"#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n",
|
||||
|
||||
note = "see \
|
||||
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
|
||||
|
@ -799,29 +800,29 @@ impl Item {
|
|||
}
|
||||
|
||||
Some(MagicAttrName::NextDisplayOrder) => {
|
||||
assert_attr_kind(attr, &[AttrKind::Command]);
|
||||
assert_attr_kind(attr, &[AttrKind::Command])?;
|
||||
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr)));
|
||||
}
|
||||
|
||||
Some(MagicAttrName::NextHelpHeading) => {
|
||||
assert_attr_kind(attr, &[AttrKind::Command]);
|
||||
assert_attr_kind(attr, &[AttrKind::Command])?;
|
||||
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr)));
|
||||
}
|
||||
|
||||
Some(MagicAttrName::RenameAll) => {
|
||||
let lit = attr.lit_str_or_abort();
|
||||
self.casing = CasingStyle::from_lit(lit);
|
||||
let lit = attr.lit_str_or_abort()?;
|
||||
self.casing = CasingStyle::from_lit(lit)?;
|
||||
}
|
||||
|
||||
Some(MagicAttrName::RenameAllEnv) => {
|
||||
assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg]);
|
||||
assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?;
|
||||
|
||||
let lit = attr.lit_str_or_abort();
|
||||
self.env_casing = CasingStyle::from_lit(lit);
|
||||
let lit = attr.lit_str_or_abort()?;
|
||||
self.env_casing = CasingStyle::from_lit(lit)?;
|
||||
}
|
||||
|
||||
Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
|
||||
|
@ -839,20 +840,20 @@ impl Item {
|
|||
| Some(MagicAttrName::Author)
|
||||
| Some(MagicAttrName::Version)
|
||||
=> {
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
self.push_method(*attr.kind.get(), attr.name.clone(), expr);
|
||||
}
|
||||
|
||||
// Magic only for the default, otherwise just forward to the builder
|
||||
Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => {
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
self.push_method(*attr.kind.get(), attr.name.clone(), expr);
|
||||
}
|
||||
|
||||
// Directives that never receive a value
|
||||
Some(MagicAttrName::ValueEnum)
|
||||
| Some(MagicAttrName::VerbatimDocComment) => {
|
||||
let expr = attr.value_or_abort();
|
||||
let expr = attr.value_or_abort()?;
|
||||
abort!(expr, "attribute `{}` does not accept a value", attr.name);
|
||||
}
|
||||
|
||||
|
@ -883,6 +884,8 @@ impl Item {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) {
|
||||
|
@ -912,7 +915,7 @@ impl Item {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_kind(&mut self, kind: Sp<Kind>) {
|
||||
fn set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error> {
|
||||
match (self.kind.get(), kind.get()) {
|
||||
(Kind::Arg(_), Kind::FromGlobal(_))
|
||||
| (Kind::Arg(_), Kind::Subcommand(_))
|
||||
|
@ -932,6 +935,7 @@ impl Item {
|
|||
abort!(kind.span(), "`{}` cannot be used with `{}`", new, old);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn find_default_method(&self) -> Option<&Method> {
|
||||
|
@ -1211,19 +1215,21 @@ impl Method {
|
|||
Method { name, args }
|
||||
}
|
||||
|
||||
fn from_env(ident: Ident, env_var: &str) -> Option<Self> {
|
||||
fn from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error> {
|
||||
let mut lit = match env::var(env_var) {
|
||||
Ok(val) => {
|
||||
if val.is_empty() {
|
||||
return None;
|
||||
return Ok(None);
|
||||
}
|
||||
LitStr::new(&val, ident.span())
|
||||
}
|
||||
Err(_) => {
|
||||
abort!(ident,
|
||||
"cannot derive `{}` from Cargo.toml", ident;
|
||||
note = "`{}` environment variable is not set", env_var;
|
||||
help = "use `{} = \"...\"` to set {} manually", ident, ident;
|
||||
abort!(
|
||||
ident,
|
||||
"cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n",
|
||||
ident,
|
||||
note = format_args!("`{}` environment variable is not set", env_var),
|
||||
help = format_args!("use `{} = \"...\"` to set {} manually", ident, ident)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -1233,7 +1239,7 @@ impl Method {
|
|||
lit = LitStr::new(&edited, lit.span());
|
||||
}
|
||||
|
||||
Some(Method::new(ident, quote!(#lit)))
|
||||
Ok(Some(Method::new(ident, quote!(#lit))))
|
||||
}
|
||||
|
||||
pub(crate) fn args(&self) -> &TokenStream {
|
||||
|
@ -1299,7 +1305,7 @@ impl ToTokens for Deprecation {
|
|||
}
|
||||
}
|
||||
|
||||
fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) {
|
||||
fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> {
|
||||
if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt {
|
||||
// deprecated
|
||||
} else if !possible_kind.contains(attr.kind.get()) {
|
||||
|
@ -1315,6 +1321,7 @@ fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) {
|
|||
options.join(", ")
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// replace all `:` with `, ` when not inside the `<>`
|
||||
|
@ -1364,13 +1371,13 @@ pub enum CasingStyle {
|
|||
}
|
||||
|
||||
impl CasingStyle {
|
||||
fn from_lit(name: &LitStr) -> Sp<Self> {
|
||||
fn from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error> {
|
||||
use self::CasingStyle::*;
|
||||
|
||||
let normalized = name.value().to_upper_camel_case().to_lowercase();
|
||||
let cs = |kind| Sp::new(kind, name.span());
|
||||
|
||||
match normalized.as_ref() {
|
||||
let s = match normalized.as_ref() {
|
||||
"camel" | "camelcase" => cs(Camel),
|
||||
"kebab" | "kebabcase" => cs(Kebab),
|
||||
"pascal" | "pascalcase" => cs(Pascal),
|
||||
|
@ -1380,7 +1387,8 @@ impl CasingStyle {
|
|||
"upper" | "uppercase" => cs(Upper),
|
||||
"verbatim" | "verbatimcase" => cs(Verbatim),
|
||||
s => abort!(name, "unsupported casing: `{}`", s),
|
||||
}
|
||||
};
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_error::proc_macro_error;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use syn::{Data, DataStruct, Fields};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod attr;
|
||||
mod derives;
|
||||
|
@ -30,10 +33,14 @@ mod utils;
|
|||
|
||||
/// Generates the `ValueEnum` impl.
|
||||
#[proc_macro_derive(ValueEnum, attributes(clap, value))]
|
||||
#[proc_macro_error]
|
||||
pub fn value_enum(input: TokenStream) -> TokenStream {
|
||||
let input: DeriveInput = parse_macro_input!(input);
|
||||
derives::derive_value_enum(&input).into()
|
||||
derives::derive_value_enum(&input)
|
||||
.unwrap_or_else(|err| {
|
||||
let dummy = dummies::value_enum(&input.ident);
|
||||
to_compile_error(err, dummy)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Generates the `Parser` implementation.
|
||||
|
@ -43,24 +50,67 @@ pub fn value_enum(input: TokenStream) -> TokenStream {
|
|||
/// implementing a conversion code to instantiate an instance of the user
|
||||
/// context struct.
|
||||
#[proc_macro_derive(Parser, attributes(clap, structopt, command, arg, group))]
|
||||
#[proc_macro_error]
|
||||
pub fn parser(input: TokenStream) -> TokenStream {
|
||||
let input: DeriveInput = parse_macro_input!(input);
|
||||
derives::derive_parser(&input).into()
|
||||
derives::derive_parser(&input)
|
||||
.unwrap_or_else(|err| {
|
||||
let specific_dummy = match input.data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(ref _fields),
|
||||
..
|
||||
}) => Some(dummies::args(&input.ident)),
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Unit,
|
||||
..
|
||||
}) => Some(dummies::args(&input.ident)),
|
||||
Data::Enum(_) => Some(dummies::subcommand(&input.ident)),
|
||||
_ => None,
|
||||
};
|
||||
let dummy = specific_dummy
|
||||
.map(|specific_dummy| {
|
||||
let parser_dummy = dummies::parser(&input.ident);
|
||||
quote::quote! {
|
||||
#parser_dummy
|
||||
#specific_dummy
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| quote::quote!());
|
||||
to_compile_error(err, dummy)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Generates the `Subcommand` impl.
|
||||
#[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);
|
||||
derives::derive_subcommand(&input).into()
|
||||
derives::derive_subcommand(&input)
|
||||
.unwrap_or_else(|err| {
|
||||
let dummy = dummies::subcommand(&input.ident);
|
||||
to_compile_error(err, dummy)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Generates the `Args` impl.
|
||||
#[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);
|
||||
derives::derive_args(&input).into()
|
||||
derives::derive_args(&input)
|
||||
.unwrap_or_else(|err| {
|
||||
let dummy = dummies::args(&input.ident);
|
||||
to_compile_error(err, dummy)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
fn to_compile_error(
|
||||
error: syn::Error,
|
||||
dummy: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let compile_errors = error.to_compile_error();
|
||||
quote::quote!(
|
||||
#dummy
|
||||
#compile_errors
|
||||
)
|
||||
}
|
||||
|
|
21
clap_derive/src/macros.rs
Normal file
21
clap_derive/src/macros.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
macro_rules! format_err {
|
||||
($obj:expr, $($format:tt)+) => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::utils::error::*;
|
||||
let msg = format!($($format)+);
|
||||
$obj.EXPECTED_Span_OR_ToTokens(msg)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! abort {
|
||||
($obj:expr, $($format:tt)+) => {{
|
||||
return Err(format_err!($obj, $($format)+));
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! abort_call_site {
|
||||
($($format:tt)+) => {{
|
||||
let span = proc_macro2::Span::call_site();
|
||||
abort!(span, $($format)+)
|
||||
}};
|
||||
}
|
22
clap_derive/src/utils/error.rs
Normal file
22
clap_derive/src/utils/error.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
pub trait SpanError {
|
||||
#[allow(non_snake_case)]
|
||||
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error;
|
||||
}
|
||||
|
||||
pub trait ToTokensError {
|
||||
#[allow(non_snake_case)]
|
||||
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error;
|
||||
}
|
||||
|
||||
impl<T: quote::ToTokens> ToTokensError for T {
|
||||
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error {
|
||||
// Curb monomorphization from generating too many identical `new_spanned`.
|
||||
syn::Error::new_spanned(self.to_token_stream(), msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl SpanError for proc_macro2::Span {
|
||||
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error {
|
||||
syn::Error::new(*self, msg)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
pub mod error;
|
||||
|
||||
mod doc_comments;
|
||||
mod spanned;
|
||||
mod ty;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
error: #[arg(default_values_t)] can be used only on Vec types
|
||||
|
||||
= note: see https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes
|
||||
= note: see https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes
|
||||
|
||||
--> tests/derive_ui/default_values_t_invalid.rs:6:11
|
||||
|
|
||||
|
|
Loading…
Reference in a new issue