// 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::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
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());
letmut 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, "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::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 = <#tyas 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 = <#tyas clap::Subcommand>::augment_subcommands(#app_var);
}
};
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].ty; if override_required {
quote_spanned! { ty.span()=>
{
<#tyas clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
}
}
} else {
quote_spanned! { ty.span()=>
{
<#tyas 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 = collect_args_fields(item, fields)?;
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
}
}
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { let ty = &unnamed[0].ty; let arg_block = if override_required {
quote_spanned! { ty.span()=>
{
<#tyas clap::Args>::augment_args_for_update(#subcommand_var)
}
}
} else {
quote_spanned! { ty.span()=>
{
<#tyas 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")
}
};
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)
}
};
subcommands.push(genned);
}
let deprecations = if !override_required {
parent_item.deprecations()
} else {
quote!()
}; let initial_app_methods = parent_item.initial_top_level_methods(); let final_app_methods = parent_item.final_top_level_methods();
Ok(quote! { #deprecations; let#app_var = #app_var#initial_app_methods; #(#subcommands )*; #app_var#final_app_methods
})
}
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> { use syn::Fields::Unnamed;
letmut 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,
let subcommands = variants.iter().map(|(_variant, item)| { let sub_name = item.cased_name();
quote! { if#sub_name == __clap_name { returntrue
}
}
}); 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 <#tyas clap::Subcommand>::has_subcommand(__clap_name) { returntrue;
}
})
}
_ => abort!(
variant, "`flatten` is usable only with single-typed tuple variants"
),
})
.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");
letmut ext_subcmd = None; letmut flatten_variants = Vec::new(); letmut 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,
_ => 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))
} elseif 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>` \
to be used with `external_subcommand`."
),
};
#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};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.