1 use std::iter::FromIterator; 2 3 use proc_macro2::TokenStream; 4 use proc_macro_error::abort; 5 use proc_macro_error::ResultExt; 6 use quote::quote; 7 use quote::ToTokens; 8 use syn::spanned::Spanned; 9 use syn::{ 10 parenthesized, 11 parse::{Parse, ParseStream}, 12 punctuated::Punctuated, 13 Attribute, Expr, Ident, LitStr, Token, 14 }; 15 16 use crate::utils::Sp; 17 18 #[derive(Clone)] 19 pub struct ClapAttr { 20 pub kind: Sp<AttrKind>, 21 pub name: Ident, 22 pub magic: Option<MagicAttrName>, 23 pub value: Option<AttrValue>, 24 } 25 26 impl ClapAttr { parse_all(all_attrs: &[Attribute]) -> Vec<Self>27 pub fn parse_all(all_attrs: &[Attribute]) -> Vec<Self> { 28 all_attrs 29 .iter() 30 .filter_map(|attr| { 31 let kind = if attr.path.is_ident("clap") { 32 Some(Sp::new(AttrKind::Clap, attr.path.span())) 33 } else if attr.path.is_ident("structopt") { 34 Some(Sp::new(AttrKind::StructOpt, attr.path.span())) 35 } else if attr.path.is_ident("command") { 36 Some(Sp::new(AttrKind::Command, attr.path.span())) 37 } else if attr.path.is_ident("group") { 38 Some(Sp::new(AttrKind::Group, attr.path.span())) 39 } else if attr.path.is_ident("arg") { 40 Some(Sp::new(AttrKind::Arg, attr.path.span())) 41 } else if attr.path.is_ident("value") { 42 Some(Sp::new(AttrKind::Value, attr.path.span())) 43 } else { 44 None 45 }; 46 kind.map(|k| (k, attr)) 47 }) 48 .flat_map(|(k, attr)| { 49 attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated) 50 .unwrap_or_abort() 51 .into_iter() 52 .map(move |mut a| { 53 a.kind = k; 54 a 55 }) 56 }) 57 .collect() 58 } 59 value_or_abort(&self) -> &AttrValue60 pub fn value_or_abort(&self) -> &AttrValue { 61 self.value 62 .as_ref() 63 .unwrap_or_else(|| abort!(self.name, "attribute `{}` requires a value", self.name)) 64 } 65 lit_str_or_abort(&self) -> &LitStr66 pub fn lit_str_or_abort(&self) -> &LitStr { 67 let value = self.value_or_abort(); 68 match value { 69 AttrValue::LitStr(tokens) => tokens, 70 AttrValue::Expr(_) | AttrValue::Call(_) => { 71 abort!( 72 self.name, 73 "attribute `{}` can only accept string literals", 74 self.name 75 ) 76 } 77 } 78 } 79 } 80 81 impl Parse for ClapAttr { parse(input: ParseStream) -> syn::Result<Self>82 fn parse(input: ParseStream) -> syn::Result<Self> { 83 let name: Ident = input.parse()?; 84 let name_str = name.to_string(); 85 86 let magic = match name_str.as_str() { 87 "rename_all" => Some(MagicAttrName::RenameAll), 88 "rename_all_env" => Some(MagicAttrName::RenameAllEnv), 89 "skip" => Some(MagicAttrName::Skip), 90 "next_display_order" => Some(MagicAttrName::NextDisplayOrder), 91 "next_help_heading" => Some(MagicAttrName::NextHelpHeading), 92 "default_value_t" => Some(MagicAttrName::DefaultValueT), 93 "default_values_t" => Some(MagicAttrName::DefaultValuesT), 94 "default_value_os_t" => Some(MagicAttrName::DefaultValueOsT), 95 "default_values_os_t" => Some(MagicAttrName::DefaultValuesOsT), 96 "long" => Some(MagicAttrName::Long), 97 "short" => Some(MagicAttrName::Short), 98 "value_parser" => Some(MagicAttrName::ValueParser), 99 "action" => Some(MagicAttrName::Action), 100 "env" => Some(MagicAttrName::Env), 101 "flatten" => Some(MagicAttrName::Flatten), 102 "value_enum" => Some(MagicAttrName::ValueEnum), 103 "from_global" => Some(MagicAttrName::FromGlobal), 104 "subcommand" => Some(MagicAttrName::Subcommand), 105 "external_subcommand" => Some(MagicAttrName::ExternalSubcommand), 106 "verbatim_doc_comment" => Some(MagicAttrName::VerbatimDocComment), 107 "about" => Some(MagicAttrName::About), 108 "long_about" => Some(MagicAttrName::LongAbout), 109 "long_help" => Some(MagicAttrName::LongHelp), 110 "author" => Some(MagicAttrName::Author), 111 "version" => Some(MagicAttrName::Version), 112 _ => None, 113 }; 114 115 let value = if input.peek(Token![=]) { 116 // `name = value` attributes. 117 let assign_token = input.parse::<Token![=]>()?; // skip '=' 118 if input.peek(LitStr) { 119 let lit: LitStr = input.parse()?; 120 Some(AttrValue::LitStr(lit)) 121 } else { 122 match input.parse::<Expr>() { 123 Ok(expr) => Some(AttrValue::Expr(expr)), 124 125 Err(_) => abort! { 126 assign_token, 127 "expected `string literal` or `expression` after `=`" 128 }, 129 } 130 } 131 } else if input.peek(syn::token::Paren) { 132 // `name(...)` attributes. 133 let nested; 134 parenthesized!(nested in input); 135 136 let method_args: Punctuated<_, Token![,]> = nested.parse_terminated(Expr::parse)?; 137 Some(AttrValue::Call(Vec::from_iter(method_args))) 138 } else { 139 None 140 }; 141 142 Ok(Self { 143 kind: Sp::new(AttrKind::Clap, name.span()), 144 name, 145 magic, 146 value, 147 }) 148 } 149 } 150 151 #[derive(Copy, Clone, PartialEq, Eq)] 152 pub enum MagicAttrName { 153 Short, 154 Long, 155 ValueParser, 156 Action, 157 Env, 158 Flatten, 159 ValueEnum, 160 FromGlobal, 161 Subcommand, 162 VerbatimDocComment, 163 ExternalSubcommand, 164 About, 165 LongAbout, 166 LongHelp, 167 Author, 168 Version, 169 RenameAllEnv, 170 RenameAll, 171 Skip, 172 DefaultValueT, 173 DefaultValuesT, 174 DefaultValueOsT, 175 DefaultValuesOsT, 176 NextDisplayOrder, 177 NextHelpHeading, 178 } 179 180 #[derive(Clone)] 181 #[allow(clippy::large_enum_variant)] 182 pub enum AttrValue { 183 LitStr(LitStr), 184 Expr(Expr), 185 Call(Vec<Expr>), 186 } 187 188 impl ToTokens for AttrValue { to_tokens(&self, tokens: &mut TokenStream)189 fn to_tokens(&self, tokens: &mut TokenStream) { 190 match self { 191 Self::LitStr(t) => t.to_tokens(tokens), 192 Self::Expr(t) => t.to_tokens(tokens), 193 Self::Call(t) => { 194 let t = quote!(#(#t),*); 195 t.to_tokens(tokens) 196 } 197 } 198 } 199 } 200 201 #[derive(Copy, Clone, PartialEq, Eq)] 202 pub enum AttrKind { 203 Clap, 204 StructOpt, 205 Command, 206 Group, 207 Arg, 208 Value, 209 } 210 211 impl AttrKind { as_str(&self) -> &'static str212 pub fn as_str(&self) -> &'static str { 213 match self { 214 Self::Clap => "clap", 215 Self::StructOpt => "structopt", 216 Self::Command => "command", 217 Self::Group => "group", 218 Self::Arg => "arg", 219 Self::Value => "value", 220 } 221 } 222 } 223