1 use proc_macro2::TokenStream;
2 use syn::{
3     parenthesized,
4     parse::{Parse, ParseStream},
5     parse2, parse_str,
6     punctuated::Punctuated,
7     Attribute, DeriveInput, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue, Path,
8     Token, Variant, Visibility,
9 };
10 
11 use super::case_style::CaseStyle;
12 
13 pub mod kw {
14     use syn::custom_keyword;
15     pub use syn::token::Crate;
16 
17     // enum metadata
18     custom_keyword!(serialize_all);
19     custom_keyword!(use_phf);
20 
21     // enum discriminant metadata
22     custom_keyword!(derive);
23     custom_keyword!(name);
24     custom_keyword!(vis);
25 
26     // variant metadata
27     custom_keyword!(message);
28     custom_keyword!(detailed_message);
29     custom_keyword!(serialize);
30     custom_keyword!(to_string);
31     custom_keyword!(disabled);
32     custom_keyword!(default);
33     custom_keyword!(props);
34     custom_keyword!(ascii_case_insensitive);
35 }
36 
37 pub enum EnumMeta {
38     SerializeAll {
39         kw: kw::serialize_all,
40         case_style: CaseStyle,
41     },
42     AsciiCaseInsensitive(kw::ascii_case_insensitive),
43     Crate {
44         kw: kw::Crate,
45         crate_module_path: Path,
46     },
47     UsePhf(kw::use_phf),
48 }
49 
50 impl Parse for EnumMeta {
parse(input: ParseStream) -> syn::Result<Self>51     fn parse(input: ParseStream) -> syn::Result<Self> {
52         let lookahead = input.lookahead1();
53         if lookahead.peek(kw::serialize_all) {
54             let kw = input.parse::<kw::serialize_all>()?;
55             input.parse::<Token![=]>()?;
56             let case_style = input.parse()?;
57             Ok(EnumMeta::SerializeAll { kw, case_style })
58         } else if lookahead.peek(kw::Crate) {
59             let kw = input.parse::<kw::Crate>()?;
60             input.parse::<Token![=]>()?;
61             let path_str: LitStr = input.parse()?;
62             let path_tokens = parse_str(&path_str.value())?;
63             let crate_module_path = parse2(path_tokens)?;
64             Ok(EnumMeta::Crate {
65                 kw,
66                 crate_module_path,
67             })
68         } else if lookahead.peek(kw::ascii_case_insensitive) {
69             Ok(EnumMeta::AsciiCaseInsensitive(input.parse()?))
70         } else if lookahead.peek(kw::use_phf) {
71             Ok(EnumMeta::UsePhf(input.parse()?))
72         } else {
73             Err(lookahead.error())
74         }
75     }
76 }
77 
78 pub enum EnumDiscriminantsMeta {
79     Derive { kw: kw::derive, paths: Vec<Path> },
80     Name { kw: kw::name, name: Ident },
81     Vis { kw: kw::vis, vis: Visibility },
82     Other { path: Path, nested: TokenStream },
83 }
84 
85 impl Parse for EnumDiscriminantsMeta {
parse(input: ParseStream) -> syn::Result<Self>86     fn parse(input: ParseStream) -> syn::Result<Self> {
87         if input.peek(kw::derive) {
88             let kw = input.parse()?;
89             let content;
90             parenthesized!(content in input);
91             let paths = content.parse_terminated(Path::parse, Token![,])?;
92             Ok(EnumDiscriminantsMeta::Derive {
93                 kw,
94                 paths: paths.into_iter().collect(),
95             })
96         } else if input.peek(kw::name) {
97             let kw = input.parse()?;
98             let content;
99             parenthesized!(content in input);
100             let name = content.parse()?;
101             Ok(EnumDiscriminantsMeta::Name { kw, name })
102         } else if input.peek(kw::vis) {
103             let kw = input.parse()?;
104             let content;
105             parenthesized!(content in input);
106             let vis = content.parse()?;
107             Ok(EnumDiscriminantsMeta::Vis { kw, vis })
108         } else {
109             let path = input.parse()?;
110             let content;
111             parenthesized!(content in input);
112             let nested = content.parse()?;
113             Ok(EnumDiscriminantsMeta::Other { path, nested })
114         }
115     }
116 }
117 
118 pub trait DeriveInputExt {
119     /// Get all the strum metadata associated with an enum.
get_metadata(&self) -> syn::Result<Vec<EnumMeta>>120     fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>>;
121 
122     /// Get all the `strum_discriminants` metadata associated with an enum.
get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>123     fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>;
124 }
125 
126 impl DeriveInputExt for DeriveInput {
get_metadata(&self) -> syn::Result<Vec<EnumMeta>>127     fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>> {
128         get_metadata_inner("strum", &self.attrs)
129     }
130 
get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>131     fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>> {
132         get_metadata_inner("strum_discriminants", &self.attrs)
133     }
134 }
135 
136 pub enum VariantMeta {
137     Message {
138         kw: kw::message,
139         value: LitStr,
140     },
141     DetailedMessage {
142         kw: kw::detailed_message,
143         value: LitStr,
144     },
145     Serialize {
146         kw: kw::serialize,
147         value: LitStr,
148     },
149     Documentation {
150         value: LitStr,
151     },
152     ToString {
153         kw: kw::to_string,
154         value: LitStr,
155     },
156     Disabled(kw::disabled),
157     Default(kw::default),
158     AsciiCaseInsensitive {
159         kw: kw::ascii_case_insensitive,
160         value: bool,
161     },
162     Props {
163         kw: kw::props,
164         props: Vec<(LitStr, LitStr)>,
165     },
166 }
167 
168 impl Parse for VariantMeta {
parse(input: ParseStream) -> syn::Result<Self>169     fn parse(input: ParseStream) -> syn::Result<Self> {
170         let lookahead = input.lookahead1();
171         if lookahead.peek(kw::message) {
172             let kw = input.parse()?;
173             let _: Token![=] = input.parse()?;
174             let value = input.parse()?;
175             Ok(VariantMeta::Message { kw, value })
176         } else if lookahead.peek(kw::detailed_message) {
177             let kw = input.parse()?;
178             let _: Token![=] = input.parse()?;
179             let value = input.parse()?;
180             Ok(VariantMeta::DetailedMessage { kw, value })
181         } else if lookahead.peek(kw::serialize) {
182             let kw = input.parse()?;
183             let _: Token![=] = input.parse()?;
184             let value = input.parse()?;
185             Ok(VariantMeta::Serialize { kw, value })
186         } else if lookahead.peek(kw::to_string) {
187             let kw = input.parse()?;
188             let _: Token![=] = input.parse()?;
189             let value = input.parse()?;
190             Ok(VariantMeta::ToString { kw, value })
191         } else if lookahead.peek(kw::disabled) {
192             Ok(VariantMeta::Disabled(input.parse()?))
193         } else if lookahead.peek(kw::default) {
194             Ok(VariantMeta::Default(input.parse()?))
195         } else if lookahead.peek(kw::ascii_case_insensitive) {
196             let kw = input.parse()?;
197             let value = if input.peek(Token![=]) {
198                 let _: Token![=] = input.parse()?;
199                 input.parse::<LitBool>()?.value
200             } else {
201                 true
202             };
203             Ok(VariantMeta::AsciiCaseInsensitive { kw, value })
204         } else if lookahead.peek(kw::props) {
205             let kw = input.parse()?;
206             let content;
207             parenthesized!(content in input);
208             let props = content.parse_terminated(Prop::parse, Token![,])?;
209             Ok(VariantMeta::Props {
210                 kw,
211                 props: props
212                     .into_iter()
213                     .map(|Prop(k, v)| (LitStr::new(&k.to_string(), k.span()), v))
214                     .collect(),
215             })
216         } else {
217             Err(lookahead.error())
218         }
219     }
220 }
221 
222 struct Prop(Ident, LitStr);
223 
224 impl Parse for Prop {
parse(input: ParseStream) -> syn::Result<Self>225     fn parse(input: ParseStream) -> syn::Result<Self> {
226         use syn::ext::IdentExt;
227 
228         let k = Ident::parse_any(input)?;
229         let _: Token![=] = input.parse()?;
230         let v = input.parse()?;
231 
232         Ok(Prop(k, v))
233     }
234 }
235 
236 pub trait VariantExt {
237     /// Get all the metadata associated with an enum variant.
get_metadata(&self) -> syn::Result<Vec<VariantMeta>>238     fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>>;
239 }
240 
241 impl VariantExt for Variant {
get_metadata(&self) -> syn::Result<Vec<VariantMeta>>242     fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>> {
243         let result = get_metadata_inner("strum", &self.attrs)?;
244         self.attrs
245             .iter()
246             .filter(|attr| attr.path().is_ident("doc"))
247             .try_fold(result, |mut vec, attr| {
248                 if let Meta::NameValue(MetaNameValue {
249                     value:
250                         Expr::Lit(ExprLit {
251                             lit: Lit::Str(value),
252                             ..
253                         }),
254                     ..
255                 }) = &attr.meta
256                 {
257                     vec.push(VariantMeta::Documentation {
258                         value: value.clone(),
259                     })
260                 }
261                 Ok(vec)
262             })
263     }
264 }
265 
get_metadata_inner<'a, T: Parse>( ident: &str, it: impl IntoIterator<Item = &'a Attribute>, ) -> syn::Result<Vec<T>>266 fn get_metadata_inner<'a, T: Parse>(
267     ident: &str,
268     it: impl IntoIterator<Item = &'a Attribute>,
269 ) -> syn::Result<Vec<T>> {
270     it.into_iter()
271         .filter(|attr| attr.path().is_ident(ident))
272         .try_fold(Vec::new(), |mut vec, attr| {
273             vec.extend(attr.parse_args_with(Punctuated::<T, Token![,]>::parse_terminated)?);
274             Ok(vec)
275         })
276 }
277