chore(vendor): add anyhow crate
This commit was merged in pull request #68.
This commit is contained in:
1 parent
7a3a3d6b90
commit
37539682d4
454 files changed
+8906
-1468
No files matched your search
@@ -0,0 +1,792 @@
|
||||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::{
|
||||
Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, Generics, punctuated::Punctuated,
|
||||
spanned::Spanned, token::Comma,
|
||||
};
|
||||
|
||||
use crate::item::{Item, Kind, Name};
|
||||
use crate::utils::{Sp, Ty, inner_type, sub_type};
|
||||
|
||||
pub(crate) fn derive_args(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
|
||||
let ident = &input.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 fields = collect_args_fields(&item, fields)?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Unit,
|
||||
..
|
||||
}) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
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())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_for_struct(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
fields: &[(&Field, Item)],
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
if !matches!(&*item.kind(), Kind::Command(_)) {
|
||||
abort! { item.kind().span(),
|
||||
"`{}` cannot be used with `#[command]`",
|
||||
item.kind().name(),
|
||||
}
|
||||
}
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
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 initial_app_methods = item.initial_top_level_methods();
|
||||
let final_app_methods = item.final_top_level_methods();
|
||||
|
||||
let group_id = if item.skip_group() {
|
||||
quote!(None)
|
||||
} else {
|
||||
let group_id = item.group_id();
|
||||
quote!(Some(clap::Id::from(#group_id)))
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
#[allow(
|
||||
dead_code,
|
||||
unreachable_code,
|
||||
unused_variables,
|
||||
unused_braces,
|
||||
unused_qualifications,
|
||||
)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::pedantic,
|
||||
clippy::restriction,
|
||||
clippy::perf,
|
||||
clippy::deprecated,
|
||||
clippy::nursery,
|
||||
clippy::cargo,
|
||||
clippy::suspicious_else_formatting,
|
||||
clippy::almost_swapped,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
#[automatically_derived]
|
||||
impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
|
||||
fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
|
||||
Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
|
||||
}
|
||||
|
||||
fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
|
||||
#raw_deprecated
|
||||
let v = #item_name #constructor;
|
||||
::std::result::Result::Ok(v)
|
||||
}
|
||||
|
||||
fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
|
||||
self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
|
||||
}
|
||||
|
||||
fn update_from_arg_matches_mut(&mut self, __clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
|
||||
#raw_deprecated
|
||||
#updater
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(
|
||||
dead_code,
|
||||
unreachable_code,
|
||||
unused_variables,
|
||||
unused_braces,
|
||||
unused_qualifications,
|
||||
)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::pedantic,
|
||||
clippy::restriction,
|
||||
clippy::perf,
|
||||
clippy::deprecated,
|
||||
clippy::nursery,
|
||||
clippy::cargo,
|
||||
clippy::suspicious_else_formatting,
|
||||
clippy::almost_swapped,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
#[automatically_derived]
|
||||
impl #impl_generics clap::Args for #item_name #ty_generics #where_clause {
|
||||
fn group_id() -> Option<clap::Id> {
|
||||
#group_id
|
||||
}
|
||||
fn augment_args<'b>(#app_var: clap::Command) -> clap::Command {
|
||||
let #app_var = #app_var #initial_app_methods;
|
||||
let #app_var = #augmentation;
|
||||
#app_var #final_app_methods
|
||||
}
|
||||
fn augment_args_for_update<'b>(#app_var: clap::Command) -> clap::Command {
|
||||
let #app_var = #app_var #initial_app_methods;
|
||||
let #app_var = #augmentation_update;
|
||||
#app_var #final_app_methods
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate a block of code to add arguments/subcommands corresponding to
|
||||
/// the `fields` to an cmd.
|
||||
pub(crate) fn gen_augment(
|
||||
fields: &[(&Field, Item)],
|
||||
app_var: &Ident,
|
||||
parent_item: &Item,
|
||||
override_required: bool,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let mut subcommand_specified = false;
|
||||
let mut args = Vec::new();
|
||||
for (field, item) in fields {
|
||||
let kind = item.kind();
|
||||
let genned = match &*kind {
|
||||
Kind::Command(_)
|
||||
| Kind::Value
|
||||
| Kind::Skip(_, _)
|
||||
| Kind::FromGlobal(_)
|
||||
| Kind::ExternalSubcommand => None,
|
||||
Kind::Subcommand(ty) => {
|
||||
if subcommand_specified {
|
||||
abort!(
|
||||
field.span(),
|
||||
"`#[command(subcommand)]` can only be used once per container"
|
||||
);
|
||||
}
|
||||
subcommand_specified = true;
|
||||
|
||||
let subcmd_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
let implicit_methods = if **ty == Ty::Option {
|
||||
quote!()
|
||||
} else {
|
||||
quote_spanned! { kind.span()=>
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true)
|
||||
}
|
||||
};
|
||||
|
||||
let override_methods = if override_required {
|
||||
quote_spanned! { kind.span()=>
|
||||
.subcommand_required(false)
|
||||
.arg_required_else_help(false)
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
Some(quote! {
|
||||
let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var );
|
||||
let #app_var = #app_var
|
||||
#implicit_methods
|
||||
#override_methods;
|
||||
})
|
||||
}
|
||||
Kind::Flatten(ty) => {
|
||||
let inner_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
|
||||
let next_help_heading = item.next_help_heading();
|
||||
let next_display_order = item.next_display_order();
|
||||
let flatten_group_assert = if matches!(**ty, Ty::Option) {
|
||||
quote_spanned! { kind.span()=>
|
||||
<#inner_type as clap::Args>::group_id().expect("cannot `#[flatten]` an `Option<Args>` with `#[group(skip)]`");
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
if override_required {
|
||||
Some(quote_spanned! { kind.span()=>
|
||||
#flatten_group_assert
|
||||
let #app_var = #app_var
|
||||
#next_help_heading
|
||||
#next_display_order;
|
||||
let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var);
|
||||
})
|
||||
} else {
|
||||
Some(quote_spanned! { kind.span()=>
|
||||
#flatten_group_assert
|
||||
let #app_var = #app_var
|
||||
#next_help_heading
|
||||
#next_display_order;
|
||||
let #app_var = <#inner_type as clap::Args>::augment_args(#app_var);
|
||||
})
|
||||
}
|
||||
}
|
||||
Kind::Arg(ty) => {
|
||||
let value_parser = item.value_parser(&field.ty);
|
||||
let action = item.action(&field.ty);
|
||||
let value_name = item.value_name();
|
||||
|
||||
let implicit_methods = match **ty {
|
||||
Ty::Unit => {
|
||||
// Leaving out `value_parser` as it will always fail
|
||||
quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
#action
|
||||
}
|
||||
}
|
||||
Ty::Option => {
|
||||
quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
|
||||
Ty::OptionOption => quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
.num_args(0..=1)
|
||||
#value_parser
|
||||
#action
|
||||
},
|
||||
|
||||
Ty::OptionVec => {
|
||||
if item.is_positional() {
|
||||
quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
.num_args(1..) // action won't be sufficient for getting multiple
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
} else {
|
||||
quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ty::Vec => {
|
||||
if item.is_positional() {
|
||||
quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
.num_args(1..) // action won't be sufficient for getting multiple
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
} else {
|
||||
quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ty::VecVec | Ty::OptionVecVec => {
|
||||
quote_spanned! { ty.span() =>
|
||||
.value_name(#value_name)
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
|
||||
Ty::Other => {
|
||||
let required = item.find_default_method().is_none();
|
||||
// `ArgAction::takes_values` is assuming `ArgAction::default_value` will be
|
||||
// set though that won't always be true but this should be good enough,
|
||||
// otherwise we'll report an "arg required" error when unwrapping.
|
||||
let action_value = action.args();
|
||||
quote_spanned! { ty.span()=>
|
||||
.value_name(#value_name)
|
||||
.required(#required && #action_value.takes_values())
|
||||
#value_parser
|
||||
#action
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let id = item.id();
|
||||
let explicit_methods = item.field_methods();
|
||||
let deprecations = if !override_required {
|
||||
item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let override_methods = if override_required {
|
||||
quote_spanned! { kind.span()=>
|
||||
.required(false)
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
Some(quote_spanned! { field.span()=>
|
||||
let #app_var = #app_var.arg({
|
||||
#deprecations
|
||||
|
||||
#[allow(deprecated)]
|
||||
let arg = clap::Arg::new(#id)
|
||||
#implicit_methods;
|
||||
|
||||
let arg = arg
|
||||
#explicit_methods;
|
||||
|
||||
let arg = arg
|
||||
#override_methods;
|
||||
|
||||
arg
|
||||
});
|
||||
})
|
||||
}
|
||||
};
|
||||
args.push(genned);
|
||||
}
|
||||
|
||||
let deprecations = if !override_required {
|
||||
parent_item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let group_app_methods = if parent_item.skip_group() {
|
||||
quote!()
|
||||
} else {
|
||||
let group_id = parent_item.group_id();
|
||||
let literal_group_members = fields
|
||||
.iter()
|
||||
.filter_map(|(_field, item)| {
|
||||
let kind = item.kind();
|
||||
if matches!(*kind, Kind::Arg(_)) {
|
||||
Some(item.id())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let literal_group_members_len = literal_group_members.len();
|
||||
let mut literal_group_members = quote! {{
|
||||
let members: [clap::Id; #literal_group_members_len] = [#( clap::Id::from(#literal_group_members) ),* ];
|
||||
members
|
||||
}};
|
||||
// HACK: Validation isn't ready yet for nested arg groups, so just don't populate the group in
|
||||
// that situation
|
||||
let possible_group_members_len = fields
|
||||
.iter()
|
||||
.filter(|(_field, item)| {
|
||||
let kind = item.kind();
|
||||
matches!(*kind, Kind::Flatten(_))
|
||||
})
|
||||
.count();
|
||||
if 0 < possible_group_members_len {
|
||||
literal_group_members = quote! {{
|
||||
let members: [clap::Id; 0] = [];
|
||||
members
|
||||
}};
|
||||
}
|
||||
|
||||
let group_methods = parent_item.group_methods();
|
||||
|
||||
quote!(
|
||||
.group(
|
||||
clap::ArgGroup::new(#group_id)
|
||||
.multiple(true)
|
||||
#group_methods
|
||||
.args(#literal_group_members)
|
||||
)
|
||||
)
|
||||
};
|
||||
Ok(quote! {{
|
||||
#deprecations
|
||||
let #app_var = #app_var
|
||||
#group_app_methods
|
||||
;
|
||||
#( #args )*
|
||||
#app_var
|
||||
}})
|
||||
}
|
||||
|
||||
pub(crate) 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");
|
||||
let genned = match &*kind {
|
||||
Kind::Command(_)
|
||||
| Kind::Value
|
||||
| Kind::ExternalSubcommand => {
|
||||
abort! { kind.span(),
|
||||
"`{}` cannot be used with `#[arg]`",
|
||||
kind.name(),
|
||||
}
|
||||
}
|
||||
Kind::Subcommand(ty) => {
|
||||
let subcmd_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
match **ty {
|
||||
Ty::Option => {
|
||||
quote_spanned! { kind.span()=>
|
||||
#field_name: {
|
||||
if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) {
|
||||
Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Ty::Other => {
|
||||
quote_spanned! { kind.span()=>
|
||||
#field_name: {
|
||||
<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
|
||||
}
|
||||
}
|
||||
},
|
||||
Ty::Unit |
|
||||
Ty::Vec |
|
||||
Ty::OptionOption |
|
||||
Ty::OptionVec |
|
||||
Ty::VecVec |
|
||||
Ty::OptionVecVec => {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"`{}` is an invalid `#[command(subcommand)]` type",
|
||||
ty.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kind::Flatten(ty) => {
|
||||
let inner_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
match **ty {
|
||||
Ty::Other => {
|
||||
quote_spanned! { kind.span()=>
|
||||
#field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
|
||||
}
|
||||
},
|
||||
Ty::Option => {
|
||||
quote_spanned! { kind.span()=>
|
||||
#field_name: {
|
||||
let group_id = <#inner_type as clap::Args>::group_id()
|
||||
.expect("asserted during `Arg` creation");
|
||||
if #arg_matches.contains_id(group_id.as_str()) {
|
||||
Some(
|
||||
<#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Ty::Unit |
|
||||
Ty::Vec |
|
||||
Ty::OptionOption |
|
||||
Ty::OptionVec |
|
||||
Ty::VecVec |
|
||||
Ty::OptionVecVec => {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"`{}` is an invalid `#[command(flatten)]` type",
|
||||
ty.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Kind::Skip(val, _) => match val {
|
||||
None => quote_spanned!(kind.span()=> #field_name: Default::default()),
|
||||
Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
|
||||
},
|
||||
|
||||
Kind::Arg(ty) | Kind::FromGlobal(ty) => {
|
||||
gen_parsers(item, ty, field_name, field, None)?
|
||||
}
|
||||
};
|
||||
Ok(genned)
|
||||
}).collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
|
||||
Ok(quote! {{
|
||||
#( #fields ),*
|
||||
}})
|
||||
}
|
||||
|
||||
pub(crate) 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();
|
||||
|
||||
let access = if use_self {
|
||||
quote! {
|
||||
#[allow(non_snake_case)]
|
||||
let #field_name = &mut self.#field_name;
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let arg_matches = format_ident!("__clap_arg_matches");
|
||||
|
||||
let genned = match &*kind {
|
||||
Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => {
|
||||
abort! { kind.span(),
|
||||
"`{}` cannot be used with `#[arg]`",
|
||||
kind.name(),
|
||||
}
|
||||
}
|
||||
Kind::Subcommand(ty) => {
|
||||
let subcmd_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
|
||||
let updater = quote_spanned! { ty.span()=>
|
||||
<#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
|
||||
};
|
||||
|
||||
let updater = match **ty {
|
||||
Ty::Option => quote_spanned! { kind.span()=>
|
||||
if let Some(#field_name) = #field_name.as_mut() {
|
||||
#updater
|
||||
} else {
|
||||
*#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(
|
||||
#arg_matches
|
||||
)?);
|
||||
}
|
||||
},
|
||||
_ => quote_spanned! { kind.span()=>
|
||||
#updater
|
||||
},
|
||||
};
|
||||
|
||||
quote_spanned! { kind.span()=>
|
||||
{
|
||||
#access
|
||||
#updater
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kind::Flatten(ty) => {
|
||||
let inner_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
|
||||
let updater = quote_spanned! { ty.span()=>
|
||||
<#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
|
||||
};
|
||||
|
||||
let updater = match **ty {
|
||||
Ty::Option => quote_spanned! { kind.span()=>
|
||||
if let Some(#field_name) = #field_name.as_mut() {
|
||||
#updater
|
||||
} else {
|
||||
*#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut(
|
||||
#arg_matches
|
||||
)?);
|
||||
}
|
||||
},
|
||||
_ => quote_spanned! { kind.span()=>
|
||||
#updater
|
||||
},
|
||||
};
|
||||
|
||||
quote_spanned! { kind.span()=>
|
||||
{
|
||||
#access
|
||||
#updater
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kind::Skip(_, _) => quote!(),
|
||||
|
||||
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(
|
||||
item: &Item,
|
||||
ty: &Sp<Ty>,
|
||||
field_name: &Ident,
|
||||
field: &Field,
|
||||
update: Option<&TokenStream>,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let span = ty.span();
|
||||
let convert_type = inner_type(&field.ty);
|
||||
let id = item.id();
|
||||
let get_one = quote_spanned!(span=> remove_one::<#convert_type>);
|
||||
let get_many = quote_spanned!(span=> remove_many::<#convert_type>);
|
||||
let get_occurrences = quote_spanned!(span=> remove_occurrences::<#convert_type>);
|
||||
|
||||
// Give this identifier the same hygiene
|
||||
// as the `arg_matches` parameter definition. This
|
||||
// allows us to refer to `arg_matches` within a `quote_spanned` block
|
||||
let arg_matches = format_ident!("__clap_arg_matches");
|
||||
|
||||
let field_value = match **ty {
|
||||
Ty::Unit => {
|
||||
quote_spanned! { ty.span()=>
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
Ty::Option => {
|
||||
quote_spanned! { ty.span()=>
|
||||
#arg_matches.#get_one(#id)
|
||||
}
|
||||
}
|
||||
|
||||
Ty::OptionOption => quote_spanned! { ty.span()=>
|
||||
if #arg_matches.contains_id(#id) {
|
||||
Some(
|
||||
#arg_matches.#get_one(#id)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|
||||
Ty::OptionVec => quote_spanned! { ty.span()=>
|
||||
if #arg_matches.contains_id(#id) {
|
||||
Some(#arg_matches.#get_many(#id)
|
||||
.map(|v| v.collect::<Vec<_>>())
|
||||
.unwrap_or_else(Vec::new))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|
||||
Ty::Vec => {
|
||||
quote_spanned! { ty.span()=>
|
||||
#arg_matches.#get_many(#id)
|
||||
.map(|v| v.collect::<Vec<_>>())
|
||||
.unwrap_or_else(Vec::new)
|
||||
}
|
||||
}
|
||||
|
||||
Ty::VecVec => quote_spanned! { ty.span()=>
|
||||
#arg_matches.#get_occurrences(#id)
|
||||
.map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
|
||||
.unwrap_or_else(Vec::new)
|
||||
},
|
||||
|
||||
Ty::OptionVecVec => quote_spanned! { ty.span()=>
|
||||
#arg_matches.#get_occurrences(#id)
|
||||
.map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
|
||||
},
|
||||
|
||||
Ty::Other => {
|
||||
// Prefer `concat` where possible for reduced code size but fallback to `format!` to
|
||||
// allow non-literal `id`s
|
||||
match id {
|
||||
Name::Assigned(_) => {
|
||||
quote_spanned! { ty.span()=>
|
||||
#arg_matches.#get_one(#id)
|
||||
.ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, format!("the following required argument was not provided: {}", #id)))?
|
||||
}
|
||||
}
|
||||
Name::Derived(_) => {
|
||||
quote_spanned! { ty.span()=>
|
||||
#arg_matches.#get_one(#id)
|
||||
.ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, concat!("the following required argument was not provided: ", #id)))?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let genned = if let Some(access) = update {
|
||||
quote_spanned! { field.span()=>
|
||||
if #arg_matches.contains_id(#id) {
|
||||
#access
|
||||
*#field_name = #field_value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote_spanned!(field.span()=> #field_name: #field_value )
|
||||
};
|
||||
Ok(genned)
|
||||
}
|
||||
|
||||
#[cfg(feature = "raw-deprecated")]
|
||||
pub(crate) fn raw_deprecated() -> TokenStream {
|
||||
quote! {}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "raw-deprecated"))]
|
||||
pub(crate) fn raw_deprecated() -> TokenStream {
|
||||
quote! {
|
||||
#![allow(deprecated)] // Assuming any deprecation in here will be related to a deprecation in `Args`
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn collect_args_fields<'a>(
|
||||
item: &'a Item,
|
||||
fields: &'a FieldsNamed,
|
||||
) -> Result<Vec<(&'a Field, Item)>, syn::Error> {
|
||||
fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{Generics, Ident};
|
||||
|
||||
use crate::item::Item;
|
||||
|
||||
pub(crate) 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();
|
||||
let app_var = Ident::new("__clap_app", Span::call_site());
|
||||
|
||||
let tokens = quote! {
|
||||
#[allow(
|
||||
dead_code,
|
||||
unreachable_code,
|
||||
unused_variables,
|
||||
unused_braces,
|
||||
unused_qualifications,
|
||||
)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::pedantic,
|
||||
clippy::restriction,
|
||||
clippy::perf,
|
||||
clippy::deprecated,
|
||||
clippy::nursery,
|
||||
clippy::cargo,
|
||||
clippy::suspicious_else_formatting,
|
||||
clippy::almost_swapped,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
#[automatically_derived]
|
||||
impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause {
|
||||
fn command<'b>() -> clap::Command {
|
||||
let #app_var = clap::Command::new(#name);
|
||||
<Self as clap::Args>::augment_args(#app_var)
|
||||
}
|
||||
|
||||
fn command_for_update<'b>() -> clap::Command {
|
||||
let #app_var = clap::Command::new(#name);
|
||||
<Self as clap::Args>::augment_args_for_update(#app_var)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
pub(crate) 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());
|
||||
|
||||
Ok(quote! {
|
||||
#[allow(
|
||||
dead_code,
|
||||
unreachable_code,
|
||||
unused_variables,
|
||||
unused_braces,
|
||||
unused_qualifications,
|
||||
)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::pedantic,
|
||||
clippy::restriction,
|
||||
clippy::perf,
|
||||
clippy::deprecated,
|
||||
clippy::nursery,
|
||||
clippy::cargo,
|
||||
clippy::suspicious_else_formatting,
|
||||
clippy::almost_swapped,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
#[automatically_derived]
|
||||
impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause {
|
||||
fn command<'b>() -> clap::Command {
|
||||
let #app_var = clap::Command::new(#name)
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true);
|
||||
<Self as clap::Subcommand>::augment_subcommands(#app_var)
|
||||
}
|
||||
|
||||
fn command_for_update<'b>() -> clap::Command {
|
||||
let #app_var = clap::Command::new(#name);
|
||||
<Self as clap::Subcommand>::augment_subcommands_for_update(#app_var)
|
||||
.subcommand_required(false)
|
||||
.arg_required_else_help(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
mod args;
|
||||
mod into_app;
|
||||
mod parser;
|
||||
mod subcommand;
|
||||
mod value_enum;
|
||||
|
||||
pub(crate) use self::parser::derive_parser;
|
||||
pub(crate) use args::derive_args;
|
||||
pub(crate) use subcommand::derive_subcommand;
|
||||
pub(crate) use value_enum::derive_value_enum;
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
use syn::Variant;
|
||||
use syn::{
|
||||
self, Data, DataStruct, DeriveInput, Field, Fields, Generics, punctuated::Punctuated,
|
||||
token::Comma,
|
||||
};
|
||||
|
||||
use crate::derives::args::collect_args_fields;
|
||||
use crate::derives::{args, into_app, subcommand};
|
||||
use crate::item::Item;
|
||||
use crate::item::Name;
|
||||
|
||||
pub(crate) 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();
|
||||
|
||||
let pkg_name_tokens = if pkg_name.is_empty() {
|
||||
quote!(#pkg_name)
|
||||
} else {
|
||||
quote!({
|
||||
let _ = ::core::env!("CARGO_PKG_NAME");
|
||||
#pkg_name })
|
||||
};
|
||||
|
||||
match input.data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(ref fields),
|
||||
..
|
||||
}) => {
|
||||
let name = Name::Assigned(pkg_name_tokens.clone());
|
||||
let item = Item::from_args_struct(input, name)?;
|
||||
let fields = collect_args_fields(&item, fields)?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Unit,
|
||||
..
|
||||
}) => {
|
||||
let name = Name::Assigned(pkg_name_tokens.clone());
|
||||
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())?;
|
||||
Ok((field, item))
|
||||
})
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_struct(&item, ident, &input.generics, &fields)
|
||||
}
|
||||
Data::Enum(ref e) => {
|
||||
let name = Name::Assigned(pkg_name_tokens);
|
||||
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())?;
|
||||
Ok((variant, item))
|
||||
})
|
||||
.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"),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_for_struct(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
fields: &[(&Field, Item)],
|
||||
) -> 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)?;
|
||||
|
||||
Ok(quote! {
|
||||
#[automatically_derived]
|
||||
#[allow(
|
||||
unused_qualifications,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
|
||||
|
||||
#into_app
|
||||
#args
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_for_enum(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
variants: &[(&Variant, Item)],
|
||||
) -> 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)?;
|
||||
|
||||
Ok(quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
|
||||
|
||||
#into_app
|
||||
#subcommand
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,686 @@
|
||||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::{Data, DeriveInput, FieldsUnnamed, Generics, Variant, spanned::Spanned};
|
||||
|
||||
use crate::derives::args;
|
||||
use crate::derives::args::collect_args_fields;
|
||||
use crate::item::{Item, Kind, Name};
|
||||
use crate::utils::{is_simple_ty, subty_if_name};
|
||||
|
||||
pub(crate) fn derive_subcommand(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
|
||||
let ident = &input.ident;
|
||||
|
||||
match input.data {
|
||||
Data::Enum(ref e) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
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())?;
|
||||
Ok((variant, item))
|
||||
})
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
gen_for_enum(&item, ident, &input.generics, &variants)
|
||||
}
|
||||
_ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_for_enum(
|
||||
item: &Item,
|
||||
item_name: &Ident,
|
||||
generics: &Generics,
|
||||
variants: &[(&Variant, Item)],
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
if !matches!(&*item.kind(), Kind::Command(_)) {
|
||||
abort! { item.kind().span(),
|
||||
"`{}` cannot be used with `#[command]`",
|
||||
item.kind().name(),
|
||||
}
|
||||
}
|
||||
|
||||
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 augmentation = gen_augment(variants, item, false)?;
|
||||
let augmentation_update = gen_augment(variants, item, true)?;
|
||||
let has_subcommand = gen_has_subcommand(variants)?;
|
||||
let initial_app_methods = item.initial_top_level_methods();
|
||||
let final_app_methods = item.final_top_level_methods();
|
||||
|
||||
Ok(quote! {
|
||||
#[allow(
|
||||
dead_code,
|
||||
unreachable_code,
|
||||
unused_variables,
|
||||
unused_braces,
|
||||
unused_qualifications,
|
||||
)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::pedantic,
|
||||
clippy::restriction,
|
||||
clippy::perf,
|
||||
clippy::deprecated,
|
||||
clippy::nursery,
|
||||
clippy::cargo,
|
||||
clippy::suspicious_else_formatting,
|
||||
clippy::almost_swapped,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
#[automatically_derived]
|
||||
impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
|
||||
fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
|
||||
Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
|
||||
}
|
||||
|
||||
#from_arg_matches
|
||||
|
||||
fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
|
||||
self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
|
||||
}
|
||||
#update_from_arg_matches
|
||||
}
|
||||
|
||||
#[allow(
|
||||
dead_code,
|
||||
unreachable_code,
|
||||
unused_variables,
|
||||
unused_braces,
|
||||
unused_qualifications,
|
||||
)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::pedantic,
|
||||
clippy::restriction,
|
||||
clippy::perf,
|
||||
clippy::deprecated,
|
||||
clippy::nursery,
|
||||
clippy::cargo,
|
||||
clippy::suspicious_else_formatting,
|
||||
clippy::almost_swapped,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
#[automatically_derived]
|
||||
impl #impl_generics clap::Subcommand for #item_name #ty_generics #where_clause {
|
||||
fn augment_subcommands <'b>(__clap_app: clap::Command) -> clap::Command {
|
||||
let __clap_app = __clap_app #initial_app_methods;
|
||||
let __clap_app = #augmentation;
|
||||
__clap_app #final_app_methods
|
||||
}
|
||||
fn augment_subcommands_for_update <'b>(__clap_app: clap::Command) -> clap::Command {
|
||||
let __clap_app = __clap_app #initial_app_methods;
|
||||
let __clap_app = #augmentation_update;
|
||||
__clap_app #final_app_methods
|
||||
}
|
||||
fn has_subcommand(__clap_name: &str) -> bool {
|
||||
#has_subcommand
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_augment(
|
||||
variants: &[(&Variant, Item)],
|
||||
parent_item: &Item,
|
||||
override_required: bool,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::{Named, Unit, Unnamed};
|
||||
|
||||
let app_var = Ident::new("__clap_app", Span::call_site());
|
||||
|
||||
let mut subcommands = Vec::new();
|
||||
for (variant, item) in variants {
|
||||
let kind = item.kind();
|
||||
|
||||
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,
|
||||
|
||||
_ => abort!(
|
||||
variant,
|
||||
"invalid type for `#[command(external_subcommand)]`, expected a newtype variant with either a `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(),
|
||||
"invalid type for `#[command(external_subcommand)]`, expected a `Vec<_>`"
|
||||
)
|
||||
})?;
|
||||
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].ty;
|
||||
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,
|
||||
"invalid variant for `#[command(flatten)]`, expected a newtype variant"
|
||||
),
|
||||
},
|
||||
|
||||
Kind::Subcommand(_) => {
|
||||
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
|
||||
let arg_block = match variant.fields {
|
||||
Named(_) => {
|
||||
abort!(
|
||||
variant,
|
||||
"invalid variant for `#[command(subcommand)]`, expected a newtype variant"
|
||||
)
|
||||
}
|
||||
Unit => quote!( #subcommand_var ),
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0].ty;
|
||||
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,
|
||||
"invalid variant for `#[command(subcommand)]`, expected a newtype variant"
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
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, initial_app_methods, final_from_attrs) = match variant.fields {
|
||||
Named(ref fields) => {
|
||||
let fields = collect_args_fields(item, fields)?;
|
||||
let sub_augment =
|
||||
args::gen_augment(&fields, &subcommand_var, item, override_required)?;
|
||||
(
|
||||
sub_augment,
|
||||
item.initial_top_level_methods(),
|
||||
item.final_top_level_methods(),
|
||||
)
|
||||
}
|
||||
Unit => (
|
||||
quote! { #subcommand_var },
|
||||
item.initial_top_level_methods(),
|
||||
item.final_top_level_methods(),
|
||||
),
|
||||
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0].ty;
|
||||
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)
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
arg_block,
|
||||
item.initial_top_level_methods(),
|
||||
item.final_top_level_methods(),
|
||||
)
|
||||
}
|
||||
Unnamed(..) => {
|
||||
abort!(
|
||||
variant,
|
||||
"invalid variant for `#[command(subcommand)]`, expected a newtype variant"
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let sub_augment = if parent_item.defer() {
|
||||
quote! {
|
||||
#subcommand_var.defer(|#subcommand_var| { #sub_augment })
|
||||
}
|
||||
} else {
|
||||
sub_augment
|
||||
};
|
||||
|
||||
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)
|
||||
#initial_app_methods;
|
||||
let #subcommand_var = #sub_augment;
|
||||
#subcommand_var #final_from_attrs
|
||||
});
|
||||
};
|
||||
Some(subcommand)
|
||||
}
|
||||
};
|
||||
subcommands.push(genned);
|
||||
}
|
||||
|
||||
let deprecations = if !override_required {
|
||||
parent_item.deprecations()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
Ok(quote! {{
|
||||
#deprecations;
|
||||
#( #subcommands )*;
|
||||
#app_var
|
||||
}})
|
||||
}
|
||||
|
||||
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::Unnamed;
|
||||
|
||||
let mut ext_subcmd = false;
|
||||
|
||||
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 => {
|
||||
ext_subcmd = true;
|
||||
None
|
||||
}
|
||||
Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
|
||||
}
|
||||
})
|
||||
.partition(|(_, item)| {
|
||||
let kind = item.kind();
|
||||
matches!(&*kind, Kind::Flatten(_))
|
||||
});
|
||||
|
||||
let subcommands = variants.iter().map(|(_variant, item)| {
|
||||
let sub_name = item.cased_name();
|
||||
quote! {
|
||||
if #sub_name == __clap_name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
});
|
||||
let child_subcommands = flatten_variants
|
||||
.iter()
|
||||
.map(|(variant, _attrs)| match variant.fields {
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||
let ty = &fields.unnamed[0].ty;
|
||||
Ok(quote! {
|
||||
if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => abort!(
|
||||
variant,
|
||||
"invalid variant for `#[command(flatten)]`, expected newtype variant"
|
||||
),
|
||||
})
|
||||
.collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
|
||||
let genned = if ext_subcmd {
|
||||
quote! { true }
|
||||
} else {
|
||||
quote! {
|
||||
#( #subcommands )*
|
||||
|
||||
#( #child_subcommands )else*
|
||||
|
||||
false
|
||||
}
|
||||
};
|
||||
Ok(genned)
|
||||
}
|
||||
|
||||
fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::{Named, Unit, Unnamed};
|
||||
|
||||
let subcommand_name_var = format_ident!("__clap_name");
|
||||
let sub_arg_matches_var = format_ident!("__clap_arg_matches");
|
||||
|
||||
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(),
|
||||
"`#[command(external_subcommand)] can only be specified once and has already been specified"
|
||||
);
|
||||
}
|
||||
|
||||
let ty = match variant.fields {
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
|
||||
|
||||
_ => abort!(
|
||||
variant,
|
||||
"invalid type for `#[command(external_subcommand)]`, expected a newtype variant with either a `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(),
|
||||
"invalid type for `#[command(external_subcommand)]`, expected a `Vec<String>` or `Vec<OsString>`"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
None => abort!(
|
||||
ty.span(),
|
||||
"invalid type for `#[command(external_subcommand)]`, expected a `Vec<String>` or `Vec<OsString>`"
|
||||
),
|
||||
};
|
||||
|
||||
ext_subcmd = Some((span, &variant.ident, str_ty));
|
||||
}
|
||||
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 = unflatten_variants.iter().map(|(variant, item)| {
|
||||
let sub_name = item.cased_name();
|
||||
let variant_name = &variant.ident;
|
||||
let constructor_block = match variant.fields {
|
||||
Named(ref fields) => {
|
||||
let fields = collect_args_fields(item, fields)?;
|
||||
args::gen_constructor(&fields)?
|
||||
},
|
||||
Unit => quote!(),
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||
let ty = &fields.unnamed[0].ty;
|
||||
quote!( ( <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)? ) )
|
||||
}
|
||||
Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
|
||||
};
|
||||
|
||||
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].ty;
|
||||
Ok(quote! {
|
||||
if __clap_arg_matches
|
||||
.subcommand_name()
|
||||
.map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
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,
|
||||
"invalid variant for `#[command(flatten)]`, expected newtype variant"
|
||||
),
|
||||
}
|
||||
}).collect::<Result<Vec<_>, syn::Error>>()?;
|
||||
|
||||
let wildcard = match ext_subcmd {
|
||||
Some((span, var_name, str_ty)) => quote_spanned! { span=>
|
||||
::std::result::Result::Ok(Self::#var_name(
|
||||
::std::iter::once(#str_ty::from(#subcommand_name_var))
|
||||
.chain(
|
||||
#sub_arg_matches_var
|
||||
.remove_many::<#str_ty>("")
|
||||
.unwrap()
|
||||
.map(#str_ty::from)
|
||||
)
|
||||
.collect::<::std::vec::Vec<_>>()
|
||||
))
|
||||
},
|
||||
|
||||
None => quote! {
|
||||
::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::InvalidSubcommand, format!("the subcommand '{}' wasn't recognized", #subcommand_name_var)))
|
||||
},
|
||||
};
|
||||
|
||||
let raw_deprecated = args::raw_deprecated();
|
||||
Ok(quote! {
|
||||
fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
|
||||
#raw_deprecated
|
||||
|
||||
#( #child_subcommands )else*
|
||||
|
||||
if let Some((#subcommand_name_var, mut __clap_arg_sub_matches)) = __clap_arg_matches.remove_subcommand() {
|
||||
let #sub_arg_matches_var = &mut __clap_arg_sub_matches;
|
||||
#( #subcommands )*
|
||||
|
||||
#wildcard
|
||||
} else {
|
||||
::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)]) -> Result<TokenStream, syn::Error> {
|
||||
use syn::Fields::{Named, Unit, Unnamed};
|
||||
|
||||
let (flatten, variants): (Vec<_>, Vec<_>) = variants
|
||||
.iter()
|
||||
.filter_map(|(variant, item)| {
|
||||
let kind = item.kind();
|
||||
match &*kind {
|
||||
// Fallback to `from_arg_matches_mut`
|
||||
Kind::Skip(_, _)
|
||||
| Kind::Arg(_)
|
||||
| Kind::FromGlobal(_)
|
||||
| Kind::Value
|
||||
| Kind::ExternalSubcommand => None,
|
||||
Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
|
||||
}
|
||||
})
|
||||
.partition(|(_, item)| {
|
||||
let kind = item.kind();
|
||||
matches!(&*kind, Kind::Flatten(_))
|
||||
});
|
||||
|
||||
let subcommands = variants.iter().map(|(variant, item)| {
|
||||
let sub_name = item.cased_name();
|
||||
let variant_name = &variant.ident;
|
||||
let (pattern, updater) = match variant.fields {
|
||||
Named(ref fields) => {
|
||||
let field_names = fields.named.iter().map(|field| {
|
||||
field.ident.as_ref().unwrap()
|
||||
}).collect::<Vec<_>>();
|
||||
let fields = collect_args_fields(item, fields)?;
|
||||
let update = args::gen_updater(&fields, false)?;
|
||||
(quote!( { #( #field_names, )* }), quote!( { #update } ))
|
||||
}
|
||||
Unit => (quote!(), quote!({})),
|
||||
Unnamed(ref fields) => {
|
||||
if fields.unnamed.len() == 1 {
|
||||
(
|
||||
quote!((__clap_arg)),
|
||||
quote!(clap::FromArgMatches::update_from_arg_matches_mut(
|
||||
__clap_arg,
|
||||
__clap_arg_matches
|
||||
)?),
|
||||
)
|
||||
} else {
|
||||
abort_call_site!("{}: tuple enums are not supported", variant.ident)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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].ty;
|
||||
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,
|
||||
"invalid variant for `#[command(flatten)]`, expected newtype variant"
|
||||
),
|
||||
}
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let raw_deprecated = args::raw_deprecated();
|
||||
Ok(quote! {
|
||||
fn update_from_arg_matches_mut<'b>(
|
||||
&mut self,
|
||||
__clap_arg_matches: &mut clap::ArgMatches,
|
||||
) -> ::std::result::Result<(), clap::Error> {
|
||||
#raw_deprecated
|
||||
|
||||
if let Some(__clap_name) = __clap_arg_matches.subcommand_name() {
|
||||
match self {
|
||||
#( #subcommands ),*
|
||||
s => {
|
||||
#( #child_subcommands )*
|
||||
*s = <Self as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::quote_spanned;
|
||||
use syn::{Data, DeriveInput, Fields, Ident, Variant, spanned::Spanned};
|
||||
|
||||
use crate::item::{Item, Kind, Name};
|
||||
|
||||
pub(crate) fn derive_value_enum(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
|
||||
let ident = &input.ident;
|
||||
|
||||
match input.data {
|
||||
Data::Enum(ref e) => {
|
||||
let name = Name::Derived(ident.clone());
|
||||
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(crate) 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]`",
|
||||
item.kind().name(),
|
||||
}
|
||||
}
|
||||
|
||||
let lits = lits(variants)?;
|
||||
let value_variants = gen_value_variants(&lits);
|
||||
let to_possible_value = gen_to_possible_value(item, &lits);
|
||||
|
||||
Ok(quote! {
|
||||
#[allow(
|
||||
dead_code,
|
||||
unreachable_code,
|
||||
unused_variables,
|
||||
unused_braces,
|
||||
unused_qualifications,
|
||||
)]
|
||||
#[allow(
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::pedantic,
|
||||
clippy::restriction,
|
||||
clippy::perf,
|
||||
clippy::deprecated,
|
||||
clippy::nursery,
|
||||
clippy::cargo,
|
||||
clippy::suspicious_else_formatting,
|
||||
clippy::almost_swapped,
|
||||
clippy::redundant_locals,
|
||||
)]
|
||||
#[automatically_derived]
|
||||
impl clap::ValueEnum for #item_name {
|
||||
#value_variants
|
||||
#to_possible_value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn value_variants<'a>() -> &'a [Self]{
|
||||
&[#(Self::#lit),*]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream {
|
||||
let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
|
||||
|
||||
let deprecations = item.deprecations();
|
||||
|
||||
quote! {
|
||||
fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue> {
|
||||
#deprecations
|
||||
match self {
|
||||
#(Self::#variant => Some(#lit),)*
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user