diff options
Diffstat (limited to 'vendor/clap_derive/src/attr.rs')
-rw-r--r-- | vendor/clap_derive/src/attr.rs | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/vendor/clap_derive/src/attr.rs b/vendor/clap_derive/src/attr.rs new file mode 100644 index 0000000..3bc9ac0 --- /dev/null +++ b/vendor/clap_derive/src/attr.rs @@ -0,0 +1,215 @@ +use std::iter::FromIterator; + +use proc_macro2::TokenStream; +use quote::quote; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Attribute, Expr, Ident, LitStr, Token, +}; + +use crate::utils::Sp; + +#[derive(Clone)] +pub struct ClapAttr { + pub kind: Sp<AttrKind>, + pub name: Ident, + pub magic: Option<MagicAttrName>, + pub value: Option<AttrValue>, +} + +impl ClapAttr { + 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) -> Result<&AttrValue, syn::Error> { + self.value + .as_ref() + .ok_or_else(|| format_err!(self.name, "attribute `{}` requires a value", self.name)) + } + + pub fn lit_str_or_abort(&self) -> Result<&LitStr, syn::Error> { + let value = self.value_or_abort()?; + match value { + AttrValue::LitStr(tokens) => Ok(tokens), + AttrValue::Expr(_) | AttrValue::Call(_) => { + abort!( + self.name, + "attribute `{}` can only accept string literals", + self.name + ) + } + } + } +} + +impl Parse for ClapAttr { + fn parse(input: ParseStream) -> syn::Result<Self> { + let name: Ident = input.parse()?; + let name_str = name.to_string(); + + let magic = match name_str.as_str() { + "rename_all" => Some(MagicAttrName::RenameAll), + "rename_all_env" => Some(MagicAttrName::RenameAllEnv), + "skip" => Some(MagicAttrName::Skip), + "next_display_order" => Some(MagicAttrName::NextDisplayOrder), + "next_help_heading" => Some(MagicAttrName::NextHelpHeading), + "default_value_t" => Some(MagicAttrName::DefaultValueT), + "default_values_t" => Some(MagicAttrName::DefaultValuesT), + "default_value_os_t" => Some(MagicAttrName::DefaultValueOsT), + "default_values_os_t" => Some(MagicAttrName::DefaultValuesOsT), + "long" => Some(MagicAttrName::Long), + "short" => Some(MagicAttrName::Short), + "value_parser" => Some(MagicAttrName::ValueParser), + "action" => Some(MagicAttrName::Action), + "env" => Some(MagicAttrName::Env), + "flatten" => Some(MagicAttrName::Flatten), + "value_enum" => Some(MagicAttrName::ValueEnum), + "from_global" => Some(MagicAttrName::FromGlobal), + "subcommand" => Some(MagicAttrName::Subcommand), + "external_subcommand" => Some(MagicAttrName::ExternalSubcommand), + "verbatim_doc_comment" => Some(MagicAttrName::VerbatimDocComment), + "about" => Some(MagicAttrName::About), + "long_about" => Some(MagicAttrName::LongAbout), + "long_help" => Some(MagicAttrName::LongHelp), + "author" => Some(MagicAttrName::Author), + "version" => Some(MagicAttrName::Version), + _ => None, + }; + + let value = if input.peek(Token![=]) { + // `name = value` attributes. + let assign_token = input.parse::<Token![=]>()?; // skip '=' + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + Some(AttrValue::LitStr(lit)) + } else { + match input.parse::<Expr>() { + Ok(expr) => Some(AttrValue::Expr(expr)), + + Err(_) => abort! { + assign_token, + "expected `string literal` or `expression` after `=`" + }, + } + } + } else if input.peek(syn::token::Paren) { + // `name(...)` attributes. + let nested; + parenthesized!(nested in input); + + let method_args: Punctuated<_, _> = nested.parse_terminated(Expr::parse, Token![,])?; + Some(AttrValue::Call(Vec::from_iter(method_args))) + } else { + None + }; + + Ok(Self { + kind: Sp::new(AttrKind::Clap, name.span()), + name, + magic, + value, + }) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum MagicAttrName { + Short, + Long, + ValueParser, + Action, + Env, + Flatten, + ValueEnum, + FromGlobal, + Subcommand, + VerbatimDocComment, + ExternalSubcommand, + About, + LongAbout, + LongHelp, + Author, + Version, + RenameAllEnv, + RenameAll, + Skip, + DefaultValueT, + DefaultValuesT, + DefaultValueOsT, + DefaultValuesOsT, + NextDisplayOrder, + NextHelpHeading, +} + +#[derive(Clone)] +#[allow(clippy::large_enum_variant)] +pub enum AttrValue { + LitStr(LitStr), + Expr(Expr), + Call(Vec<Expr>), +} + +impl ToTokens for AttrValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::LitStr(t) => t.to_tokens(tokens), + Self::Expr(t) => t.to_tokens(tokens), + Self::Call(t) => { + let t = quote!(#(#t),*); + t.to_tokens(tokens) + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum AttrKind { + Clap, + StructOpt, + Command, + Group, + Arg, + Value, +} + +impl AttrKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::Clap => "clap", + Self::StructOpt => "structopt", + Self::Command => "command", + Self::Group => "group", + Self::Arg => "arg", + Self::Value => "value", + } + } +} |