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