1use proc_macro2::TokenStream;
2use 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
11use super::case_style::CaseStyle;
12
13pub mod kw {
14 use syn::custom_keyword;
15 pub use syn::token::Crate;
16
17 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 custom_keyword!(derive);
27 custom_keyword!(name);
28 custom_keyword!(vis);
29
30 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
43pub 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
69impl Parse for EnumMeta {
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
114pub 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
121impl Parse for EnumDiscriminantsMeta {
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
154pub trait DeriveInputExt {
155 fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>>;
157
158 fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>;
160}
161
162impl DeriveInputExt for DeriveInput {
163 fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>> {
164 get_metadata_inner("strum", &self.attrs)
165 }
166
167 fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>> {
168 get_metadata_inner("strum_discriminants", &self.attrs)
169 }
170}
171
172pub 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
209impl Parse for VariantMeta {
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
270struct Prop(Ident, Lit);
271
272impl Parse for Prop {
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
284pub trait VariantExt {
285 fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>>;
287}
288
289impl VariantExt for Variant {
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
314fn 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
326pub enum InnerVariantMeta {
327 DefaultWith { kw: kw::default_with, value: LitStr },
328}
329
330impl Parse for InnerVariantMeta {
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
344pub trait InnerVariantExt {
345 fn get_named_metadata(&self) -> syn::Result<Vec<InnerVariantMeta>>;
347}
348
349impl InnerVariantExt for Field {
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}