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