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