enum_utils/
attr.rs

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        // Iterate over `a` and `b` in `#[repr(a, b)]`
87        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    /// Returns an iterator over the items in `...` if this attribute looks like `#[enumeration(...)]`
155    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
168/// Parse an attr from the `syn::Meta` inside parens after "enumeration".
169impl 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        // Extracts a string literal from a MetaNameValue
176        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            // #[enumeration(skip)]
185            Meta::Path(path) if path.is_ident("skip") =>
186                Ok(Attr::Skip),
187
188            // #[enumeration(case_insensitive)]
189            Meta::Path(path) if path.is_ident("case_insensitive") =>
190                Ok(Attr::CaseInsensitive),
191
192            // #[enumeration(rename = "...")]
193            Meta::NameValue(MetaNameValue { path, lit, .. }) if path.is_ident("rename") =>
194                Ok(Attr::Rename(lit_val(lit)?)),
195
196            // #[enumeration(rename_all = "...")]
197            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            // #[enumeration(alias = "...")]
203            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    /// This will be `None` if no `#[repr]` was specified, or an error if parsing failed or
297    /// multiple `#[repr]`s were specified.
298    pub primitive_repr: Fallible<Option<(Primitive, syn::Path)>>,
299
300    pub variants: Vec<(&'a syn::Variant, VariantAttrs)>,
301
302    /// None if the enum is not C-like
303    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                    // An integer literal
353                    Some((_, Expr::Lit(ExprLit { lit: Lit::Int(i), .. }))) => {
354                        ds.push(i.base10_parse::<i128>().expect("Variant overflowed i128"));
355                    }
356
357                    // An expr with an unknown value (e.g. a const defined elsewhere)
358                    Some(_) => {
359                        discriminants = None;
360                    }
361
362                    // No discriminant
363                    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    /*
387    pub fn is_c_like(&self) -> bool {
388        self.discriminants.is_some()
389    }
390    */
391}