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