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