1use std::collections::{BTreeSet, LinkedList};
2use std::convert::{TryFrom, TryInto};
3use std::fmt;
4
5use failure::{bail, format_err, Fallible};
6
7#[derive(Debug, Clone, Copy)]
8pub enum Primitive {
9 U8,
10 U16,
11 U32,
12 U64,
13 U128,
14 Usize,
15 I8,
16 I16,
17 I32,
18 I64,
19 I128,
20 Isize,
21}
22
23impl TryFrom<&syn::Path> for Primitive {
24 type Error = ();
25
26 fn try_from(path: &syn::Path) -> Result<Self, Self::Error> {
27 use self::Primitive::*;
28
29 let ident = path.get_ident().ok_or(())?;
30
31 match ident.to_string().as_str() {
32 "u8" => Ok(U8),
33 "u16" => Ok(U16),
34 "u32" => Ok(U32),
35 "u64" => Ok(U64),
36 "u128" => Ok(U128),
37 "usize" => Ok(Usize),
38 "i8" => Ok(I8),
39 "i16" => Ok(I16),
40 "i32" => Ok(I32),
41 "i64" => Ok(I64),
42 "i128" => Ok(I128),
43 "isize" => Ok(Isize),
44
45 _ => Err(()),
46 }
47 }
48}
49
50impl Primitive {
51 pub fn max_value(&self) -> Option<u128> {
52 use self::Primitive::*;
53
54 match self {
55 U8 => Some(u8::max_value() as u128),
56 U16 => Some(u16::max_value() as u128),
57 U32 => Some(u32::max_value() as u128),
58 U64 => Some(u64::max_value() as u128),
59 U128 => Some(u128::max_value()),
60 I8 => Some(i8::max_value() as u128),
61 I16 => Some(i16::max_value() as u128),
62 I32 => Some(i32::max_value() as u128),
63 I64 => Some(i64::max_value() as u128),
64 I128 => Some(i128::max_value() as u128),
65 Usize | Isize => None,
66 }
67 }
68}
69
70pub fn parse_primitive_repr<'a>(attrs: impl 'a + Iterator<Item = &'a syn::Attribute>)
71 -> Fallible<Option<(Primitive, syn::Path)>>
72{
73 let mut repr = None;
74 for attr in attrs {
75 if !attr.path.is_ident("repr") {
76 continue;
77 }
78
79 let list = match attr.parse_meta()? {
80 syn::Meta::List(list) => list,
81 _ => continue,
82 };
83
84 debug_assert!(list.path.is_ident("repr"));
85
86 for arg in &list.nested {
88 match arg {
89 syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
90 match path.try_into() {
91 Ok(_) if repr.is_some() =>
92 bail!("Multiple primitive `#[repr(...)]`s"),
93 Ok(prim) => repr = Some((prim, path.clone())),
94 Err(_) => continue,
95 }
96 },
97 _ => continue,
98 }
99 }
100 }
101
102 Ok(repr)
103}
104
105pub struct RenameRule(serde_derive_internals::attr::RenameRule);
106
107impl RenameRule {
108 pub fn apply_to_variant(&self, s: &str) -> String {
109 self.0.apply_to_variant(s)
110 }
111}
112
113impl fmt::Debug for RenameRule {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 f.debug_struct("RenameRule")
116 .finish()
117 }
118}
119
120pub type ErrorList = LinkedList<failure::Error>;
121
122macro_rules! bail_list {
123 ($msg:literal $( , $args:expr )* $(,)?) => {
124 {
125 let mut list = ErrorList::new();
126 list.push_back(failure::format_err!($msg, $($args),*));
127 return Err(list);
128 }
129 }
130}
131
132#[derive(Debug)]
133pub enum Attr {
134 CaseInsensitive,
135 Skip,
136 Rename(String),
137 RenameAll(RenameRule),
138 Alias(String),
139}
140
141impl Attr {
142 pub fn parse_attrs(attr: &syn::Attribute) -> impl Iterator<Item = Fallible<Self>> {
143 use syn::NestedMeta;
144
145 Self::get_args(attr)
146 .map(|arg| {
147 match arg {
148 NestedMeta::Meta(m) => Attr::try_from(&m),
149 _ => bail!("Argument to attribute cannot be a literal"),
150 }
151 })
152 }
153
154 fn get_args(attr: &syn::Attribute) -> impl Iterator<Item = syn::NestedMeta> {
156 use syn::{token, Meta, MetaList, NestedMeta};
157
158 if let Ok(Meta::List(MetaList { path, nested, .. })) = attr.parse_meta() {
159 if path.is_ident("enumeration") {
160 return nested.into_iter();
161 }
162 }
163
164 syn::punctuated::Punctuated::<NestedMeta, token::Comma>::new().into_iter()
165 }
166}
167
168impl TryFrom<&'_ syn::Meta> for Attr {
170 type Error = failure::Error;
171
172 fn try_from(meta: &syn::Meta) -> Result<Self, Self::Error> {
173 use syn::{Lit, Meta, MetaNameValue};
174
175 let lit_val = |lit: &syn::Lit| {
177 match lit {
178 Lit::Str(v) => Ok(v.value()),
179 _ => bail!("Non-string literal"),
180 }
181 };
182
183 match meta {
184 Meta::Path(path) if path.is_ident("skip") =>
186 Ok(Attr::Skip),
187
188 Meta::Path(path) if path.is_ident("case_insensitive") =>
190 Ok(Attr::CaseInsensitive),
191
192 Meta::NameValue(MetaNameValue { path, lit, .. }) if path.is_ident("rename") =>
194 Ok(Attr::Rename(lit_val(lit)?)),
195
196 Meta::NameValue(MetaNameValue { path, lit, .. }) if path.is_ident("rename_all") => {
198 let rule = lit_val(lit)?.parse().map_err(|_| format_err!("Invalid RenameAll rule"))?;
199 Ok(Attr::RenameAll(RenameRule(rule)))
200 }
201
202 Meta::NameValue(MetaNameValue { path, lit, .. }) if path.is_ident("alias") =>
204 Ok(Attr::Alias(lit_val(lit)?)),
205
206 _ => bail!("Unknown attribute argument")
207 }
208 }
209}
210
211#[derive(Debug, Default)]
212pub struct VariantAttrs {
213 pub skip: bool,
214 pub rename: Option<String>,
215 pub aliases: BTreeSet<String>,
216}
217
218impl VariantAttrs {
219 pub fn from_attrs<T>(attrs: T) -> Result<Self, ErrorList>
220 where T: IntoIterator<Item = Fallible<Attr>>,
221 {
222 let mut ret = VariantAttrs::default();
223 let mut errors = ErrorList::default();
224 for attr in attrs {
225 match attr {
226 Ok(Attr::Skip) => ret.skip = true,
227
228 Ok(Attr::Rename(s)) => if ret.rename.is_none() {
229 ret.rename = Some(s);
230 } else {
231 errors.push_back(format_err!("Variant cannot be renamed multiple times"));
232 },
233
234 Ok(Attr::Alias(s)) => {
235 ret.aliases.insert(s);
236 },
237
238 Ok(attr) =>
239 errors.push_back(format_err!("Attribute \"{:?}\" is not valid for a variant", attr)),
240
241 Err(e) => errors.push_back(e),
242 }
243 }
244
245 if errors.is_empty() {
246 Ok(ret)
247 } else {
248 Err(errors)
249 }
250 }
251}
252
253#[derive(Default)]
254pub struct EnumAttrs {
255 pub nocase: bool,
256 pub rename_rule: Option<RenameRule>,
257}
258
259impl EnumAttrs {
260 pub fn from_attrs<T>(attrs: T) -> Result<Self, ErrorList>
261 where T: IntoIterator<Item = Fallible<Attr>>,
262 {
263 let mut ret = EnumAttrs::default();
264 let mut errors = ErrorList::default();
265 for attr in attrs {
266 match attr {
267 Ok(Attr::CaseInsensitive) => ret.nocase = true,
268
269 Ok(Attr::RenameAll(r)) => if ret.rename_rule.is_none() {
270 ret.rename_rule = Some(r);
271 } else {
272 errors.push_back(format_err!("Enum can only have a single \"rename_all\" attribute"));
273 },
274
275 Ok(attr) =>
276 errors.push_back(format_err!("Attribute \"{:?}\" is not valid for an enum", attr)),
277
278 Err(e) => errors.push_back(e),
279 }
280 }
281
282 if errors.is_empty() {
283 Ok(ret)
284 } else {
285 Err(errors)
286 }
287 }
288}
289
290pub type Discriminant = i128;
291
292pub struct Enum<'a> {
293 pub name: &'a syn::Ident,
294 pub attrs: EnumAttrs,
295
296 pub primitive_repr: Fallible<Option<(Primitive, syn::Path)>>,
299
300 pub variants: Vec<(&'a syn::Variant, VariantAttrs)>,
301
302 pub discriminants: Option<Vec<Discriminant>>,
304}
305
306impl<'a> Enum<'a> {
307 pub fn parse(input: &'a syn::DeriveInput) -> Result<Self, ErrorList> {
308 use syn::{Data, DataEnum, Expr, ExprLit, Lit};
309
310 let DataEnum { variants, .. } = match &input.data {
311 Data::Enum(e) => e,
312 _ => bail_list!("Input must be an enum"),
313 };
314
315 let mut errors = ErrorList::default();
316 let enum_attrs = EnumAttrs::from_attrs(input.attrs
317 .iter()
318 .flat_map(Attr::parse_attrs));
319
320 let enum_attrs = match enum_attrs {
321 Ok(attrs) => attrs,
322 Err(e) => {
323 errors = e;
324 Default::default()
325 }
326 };
327
328 let mut discriminants = Some(vec![]);
329 let mut parsed_variants = vec![];
330 for v in variants.iter() {
331 let attrs = VariantAttrs::from_attrs(v.attrs
332 .iter()
333 .flat_map(Attr::parse_attrs));
334
335 let attrs = match attrs {
336 Ok(a) => a,
337 Err(mut e) => {
338 errors.append(&mut e);
339 continue;
340 }
341 };
342
343 parsed_variants.push((v, attrs));
344
345 if v.fields != syn::Fields::Unit {
346 discriminants = None;
347 continue;
348 }
349
350 if let Some(ds) = discriminants.as_mut() {
351 match &v.discriminant {
352 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(i), .. }))) => {
354 ds.push(i.base10_parse::<i128>().expect("Variant overflowed i128"));
355 }
356
357 Some(_) => {
359 discriminants = None;
360 }
361
362 None => {
364 let d = ds.last().map(|&x| x + 1).unwrap_or(0);
365 ds.push(d);
366 }
367 }
368 }
369 }
370
371 let primitive_repr = parse_primitive_repr(input.attrs.iter());
372
373 if !errors.is_empty() {
374 return Err(errors);
375 }
376
377 Ok(Enum {
378 name: &input.ident,
379 attrs: enum_attrs,
380 variants: parsed_variants,
381 primitive_repr,
382 discriminants,
383 })
384 }
385
386 }