• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, Field, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue,
8     Path, 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!(const_into_str);
20     custom_keyword!(use_phf);
21     custom_keyword!(prefix);
22     custom_keyword!(parse_err_ty);
23     custom_keyword!(parse_err_fn);
24 
25     // enum discriminant metadata
26     custom_keyword!(derive);
27     custom_keyword!(name);
28     custom_keyword!(vis);
29 
30     // variant metadata
31     custom_keyword!(message);
32     custom_keyword!(detailed_message);
33     custom_keyword!(serialize);
34     custom_keyword!(to_string);
35     custom_keyword!(transparent);
36     custom_keyword!(disabled);
37     custom_keyword!(default);
38     custom_keyword!(default_with);
39     custom_keyword!(props);
40     custom_keyword!(ascii_case_insensitive);
41 }
42 
43 pub enum EnumMeta {
44     SerializeAll {
45         kw: kw::serialize_all,
46         case_style: CaseStyle,
47     },
48     AsciiCaseInsensitive(kw::ascii_case_insensitive),
49     Crate {
50         kw: kw::Crate,
51         crate_module_path: Path,
52     },
53     UsePhf(kw::use_phf),
54     Prefix {
55         kw: kw::prefix,
56         prefix: LitStr,
57     },
58     ParseErrTy {
59         kw: kw::parse_err_ty,
60         path: Path,
61     },
62     ParseErrFn {
63         kw: kw::parse_err_fn,
64         path: Path,
65     },
66     ConstIntoStr(kw::const_into_str),
67 }
68 
69 impl Parse for EnumMeta {
parse(input: ParseStream) -> syn::Result<Self>70     fn parse(input: ParseStream) -> syn::Result<Self> {
71         let lookahead = input.lookahead1();
72         if lookahead.peek(kw::serialize_all) {
73             let kw = input.parse::<kw::serialize_all>()?;
74             input.parse::<Token![=]>()?;
75             let case_style = input.parse()?;
76             Ok(EnumMeta::SerializeAll { kw, case_style })
77         } else if lookahead.peek(kw::Crate) {
78             let kw = input.parse::<kw::Crate>()?;
79             input.parse::<Token![=]>()?;
80             let path_str: LitStr = input.parse()?;
81             let path_tokens = parse_str(&path_str.value())?;
82             let crate_module_path = parse2(path_tokens)?;
83             Ok(EnumMeta::Crate {
84                 kw,
85                 crate_module_path,
86             })
87         } else if lookahead.peek(kw::ascii_case_insensitive) {
88             Ok(EnumMeta::AsciiCaseInsensitive(input.parse()?))
89         } else if lookahead.peek(kw::use_phf) {
90             Ok(EnumMeta::UsePhf(input.parse()?))
91         } else if lookahead.peek(kw::prefix) {
92             let kw = input.parse::<kw::prefix>()?;
93             input.parse::<Token![=]>()?;
94             let prefix = input.parse()?;
95             Ok(EnumMeta::Prefix { kw, prefix })
96         } else if lookahead.peek(kw::parse_err_ty) {
97             let kw = input.parse::<kw::parse_err_ty>()?;
98             input.parse::<Token![=]>()?;
99             let path: Path = input.parse()?;
100             Ok(EnumMeta::ParseErrTy { kw, path })
101         } else if lookahead.peek(kw::parse_err_fn) {
102             let kw = input.parse::<kw::parse_err_fn>()?;
103             input.parse::<Token![=]>()?;
104             let path: Path = input.parse()?;
105             Ok(EnumMeta::ParseErrFn { kw, path })
106         } else if lookahead.peek(kw::const_into_str) {
107             Ok(EnumMeta::ConstIntoStr(input.parse()?))
108         } else {
109             Err(lookahead.error())
110         }
111     }
112 }
113 
114 pub enum EnumDiscriminantsMeta {
115     Derive { _kw: kw::derive, paths: Vec<Path> },
116     Name { kw: kw::name, name: Ident },
117     Vis { kw: kw::vis, vis: Visibility },
118     Other { path: Path, nested: TokenStream },
119 }
120 
121 impl Parse for EnumDiscriminantsMeta {
parse(input: ParseStream) -> syn::Result<Self>122     fn parse(input: ParseStream) -> syn::Result<Self> {
123         if input.peek(kw::derive) {
124             let _kw = input.parse()?;
125             let content;
126             parenthesized!(content in input);
127             let paths = content.parse_terminated(Path::parse, Token![,])?;
128             Ok(EnumDiscriminantsMeta::Derive {
129                 _kw,
130                 paths: paths.into_iter().collect(),
131             })
132         } else if input.peek(kw::name) {
133             let kw = input.parse()?;
134             let content;
135             parenthesized!(content in input);
136             let name = content.parse()?;
137             Ok(EnumDiscriminantsMeta::Name { kw, name })
138         } else if input.peek(kw::vis) {
139             let kw = input.parse()?;
140             let content;
141             parenthesized!(content in input);
142             let vis = content.parse()?;
143             Ok(EnumDiscriminantsMeta::Vis { kw, vis })
144         } else {
145             let path = input.parse()?;
146             let content;
147             parenthesized!(content in input);
148             let nested = content.parse()?;
149             Ok(EnumDiscriminantsMeta::Other { path, nested })
150         }
151     }
152 }
153 
154 pub trait DeriveInputExt {
155     /// Get all the strum metadata associated with an enum.
get_metadata(&self) -> syn::Result<Vec<EnumMeta>>156     fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>>;
157 
158     /// Get all the `strum_discriminants` metadata associated with an enum.
get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>159     fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>;
160 }
161 
162 impl DeriveInputExt for DeriveInput {
get_metadata(&self) -> syn::Result<Vec<EnumMeta>>163     fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>> {
164         get_metadata_inner("strum", &self.attrs)
165     }
166 
get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>167     fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>> {
168         get_metadata_inner("strum_discriminants", &self.attrs)
169     }
170 }
171 
172 pub enum VariantMeta {
173     Message {
174         kw: kw::message,
175         value: LitStr,
176     },
177     DetailedMessage {
178         kw: kw::detailed_message,
179         value: LitStr,
180     },
181     Serialize {
182         _kw: kw::serialize,
183         value: LitStr,
184     },
185     Documentation {
186         value: LitStr,
187     },
188     ToString {
189         kw: kw::to_string,
190         value: LitStr,
191     },
192     Transparent(kw::transparent),
193     Disabled(kw::disabled),
194     Default(kw::default),
195     DefaultWith {
196         kw: kw::default_with,
197         value: LitStr,
198     },
199     AsciiCaseInsensitive {
200         kw: kw::ascii_case_insensitive,
201         value: bool,
202     },
203     Props {
204         _kw: kw::props,
205         props: Vec<(LitStr, Lit)>,
206     },
207 }
208 
209 impl Parse for VariantMeta {
parse(input: ParseStream) -> syn::Result<Self>210     fn parse(input: ParseStream) -> syn::Result<Self> {
211         let lookahead = input.lookahead1();
212         if lookahead.peek(kw::message) {
213             let kw = input.parse()?;
214             let _: Token![=] = input.parse()?;
215             let value = input.parse()?;
216             Ok(VariantMeta::Message { kw, value })
217         } else if lookahead.peek(kw::detailed_message) {
218             let kw = input.parse()?;
219             let _: Token![=] = input.parse()?;
220             let value = input.parse()?;
221             Ok(VariantMeta::DetailedMessage { kw, value })
222         } else if lookahead.peek(kw::serialize) {
223             let _kw = input.parse()?;
224             let _: Token![=] = input.parse()?;
225             let value = input.parse()?;
226             Ok(VariantMeta::Serialize { _kw, value })
227         } else if lookahead.peek(kw::to_string) {
228             let kw = input.parse()?;
229             let _: Token![=] = input.parse()?;
230             let value = input.parse()?;
231             Ok(VariantMeta::ToString { kw, value })
232         } else if lookahead.peek(kw::transparent) {
233             Ok(VariantMeta::Transparent(input.parse()?))
234         } else if lookahead.peek(kw::disabled) {
235             Ok(VariantMeta::Disabled(input.parse()?))
236         } else if lookahead.peek(kw::default) {
237             Ok(VariantMeta::Default(input.parse()?))
238         } else if lookahead.peek(kw::default_with) {
239             let kw = input.parse()?;
240             let _: Token![=] = input.parse()?;
241             let value = input.parse()?;
242             Ok(VariantMeta::DefaultWith { kw, value })
243         } else if lookahead.peek(kw::ascii_case_insensitive) {
244             let kw = input.parse()?;
245             let value = if input.peek(Token![=]) {
246                 let _: Token![=] = input.parse()?;
247                 input.parse::<LitBool>()?.value
248             } else {
249                 true
250             };
251             Ok(VariantMeta::AsciiCaseInsensitive { kw, value })
252         } else if lookahead.peek(kw::props) {
253             let _kw = input.parse()?;
254             let content;
255             parenthesized!(content in input);
256             let props = content.parse_terminated(Prop::parse, Token![,])?;
257             Ok(VariantMeta::Props {
258                 _kw,
259                 props: props
260                     .into_iter()
261                     .map(|Prop(k, v)| (LitStr::new(&k.to_string(), k.span()), v))
262                     .collect(),
263             })
264         } else {
265             Err(lookahead.error())
266         }
267     }
268 }
269 
270 struct Prop(Ident, Lit);
271 
272 impl Parse for Prop {
parse(input: ParseStream) -> syn::Result<Self>273     fn parse(input: ParseStream) -> syn::Result<Self> {
274         use syn::ext::IdentExt;
275 
276         let k = Ident::parse_any(input)?;
277         let _: Token![=] = input.parse()?;
278         let v = input.parse()?;
279 
280         Ok(Prop(k, v))
281     }
282 }
283 
284 pub trait VariantExt {
285     /// Get all the metadata associated with an enum variant.
get_metadata(&self) -> syn::Result<Vec<VariantMeta>>286     fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>>;
287 }
288 
289 impl VariantExt for Variant {
get_metadata(&self) -> syn::Result<Vec<VariantMeta>>290     fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>> {
291         let result = get_metadata_inner("strum", &self.attrs)?;
292         self.attrs
293             .iter()
294             .filter(|attr| attr.meta.path().is_ident("doc"))
295             .try_fold(result, |mut vec, attr| {
296                 if let Meta::NameValue(MetaNameValue {
297                     value:
298                         Expr::Lit(ExprLit {
299                             lit: Lit::Str(value),
300                             ..
301                         }),
302                     ..
303                 }) = &attr.meta
304                 {
305                     vec.push(VariantMeta::Documentation {
306                         value: value.clone(),
307                     })
308                 }
309                 Ok(vec)
310             })
311     }
312 }
313 
get_metadata_inner<'a, T: Parse>( ident: &str, it: impl IntoIterator<Item = &'a Attribute>, ) -> syn::Result<Vec<T>>314 fn get_metadata_inner<'a, T: Parse>(
315     ident: &str,
316     it: impl IntoIterator<Item = &'a Attribute>,
317 ) -> syn::Result<Vec<T>> {
318     it.into_iter()
319         .filter(|attr| attr.path().is_ident(ident))
320         .try_fold(Vec::new(), |mut vec, attr| {
321             vec.extend(attr.parse_args_with(Punctuated::<T, Token![,]>::parse_terminated)?);
322             Ok(vec)
323         })
324 }
325 
326 pub enum InnerVariantMeta {
327     DefaultWith { kw: kw::default_with, value: LitStr },
328 }
329 
330 impl Parse for InnerVariantMeta {
parse(input: ParseStream) -> syn::Result<Self>331     fn parse(input: ParseStream) -> syn::Result<Self> {
332         let lookahead = input.lookahead1();
333         if lookahead.peek(kw::default_with) {
334             let kw = input.parse()?;
335             let _: Token![=] = input.parse()?;
336             let value = input.parse()?;
337             Ok(InnerVariantMeta::DefaultWith { kw, value })
338         } else {
339             Err(lookahead.error())
340         }
341     }
342 }
343 
344 pub trait InnerVariantExt {
345     /// Get all the metadata associated with an enum variant inner.
get_named_metadata(&self) -> syn::Result<Vec<InnerVariantMeta>>346     fn get_named_metadata(&self) -> syn::Result<Vec<InnerVariantMeta>>;
347 }
348 
349 impl InnerVariantExt for Field {
get_named_metadata(&self) -> syn::Result<Vec<InnerVariantMeta>>350     fn get_named_metadata(&self) -> syn::Result<Vec<InnerVariantMeta>> {
351         let result = get_metadata_inner("strum", &self.attrs)?;
352         self.attrs
353             .iter()
354             .filter(|attr| attr.meta.path().is_ident("default_with"))
355             .try_fold(result, |vec, _attr| Ok(vec))
356     }
357 }
358