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