int_enum_impl/
ast.rs

1use core::fmt;
2
3use proc_macro2::{Ident, TokenStream};
4use quote::{IdentFragment, ToTokens};
5use syn::{Data, DeriveInput, Error, Expr, Meta, NestedMeta, Result};
6
7pub struct Input<'a> {
8    pub original: &'a DeriveInput,
9    pub ident: Ident,
10    pub repr: Repr,
11    pub variants: Vec<(Ident, Expr)>,
12}
13
14pub struct Repr {
15    pub ident: Ident,
16    pub style: IntStyle,
17    pub size: IntSize,
18}
19
20#[derive(Clone, Copy, Eq, PartialEq)]
21pub enum IntStyle {
22    Signed,
23    Unsigned,
24}
25
26#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
27pub enum IntSize {
28    _8 = 8,
29    _16 = 16,
30    _32 = 32,
31    _64 = 64,
32    _128 = 128,
33    Size,
34}
35
36impl<'a> Input<'a> {
37    pub fn from_syn(node: &'a DeriveInput) -> Result<Self> {
38        let data = match &node.data {
39            Data::Enum(data) => Ok(data),
40            _ => Err(Error::new_spanned(node, "only enums are supported")),
41        }?;
42
43        let repr = Repr::from_attrs(node)?;
44
45        let mut variants = Ok(Vec::with_capacity(data.variants.len()));
46        for variant in &data.variants {
47            match &variant.discriminant {
48                Some((_, discr)) => {
49                    if let Ok(variants) = &mut variants {
50                        variants.push((variant.ident.clone(), discr.clone()));
51                    }
52                }
53                None => {
54                    let err = Error::new_spanned(variant, "missing discriminator");
55                    match &mut variants {
56                        Ok(_) => variants = Err(err),
57                        Err(errs) => errs.combine(err),
58                    }
59                }
60            }
61        }
62        let variants = variants?;
63
64        Ok(Input { original: node, ident: node.ident.clone(), repr, variants })
65    }
66}
67
68const INVALID_REPR: &str = "invalid repr, expected one of:
69    i8, i16, i32, i64, i128, isize,
70    u8, u16, u32, u64, u128, usize";
71
72impl Repr {
73    fn from_attrs(node: &DeriveInput) -> Result<Self> {
74        for attr in &node.attrs {
75            if let Meta::List(meta) = attr.parse_meta()? {
76                // We only care about `#[repr(...)]` attributes.
77                if !meta.path.is_ident("repr") {
78                    continue;
79                }
80
81                let mut iter = meta.nested.iter();
82
83                let repr = match iter.next() {
84                    Some(r) => r,
85                    // Ignore empty `#[repr()]`.
86                    None => continue,
87                };
88
89                if iter.next().is_some() {
90                    return Err(Error::new_spanned(&meta.nested, "conflicting reprs"));
91                }
92
93                let repr = match repr {
94                    NestedMeta::Meta(Meta::Path(path)) => path.get_ident(),
95                    _ => None,
96                }
97                .ok_or_else(|| Error::new_spanned(repr, INVALID_REPR))?;
98
99                return Repr::from_ident(repr);
100            }
101        }
102
103        Err(Error::new_spanned(node, "missing `repr` attribute"))
104    }
105
106    fn from_ident(repr: &Ident) -> Result<Self> {
107        fn invalid_repr(repr: &Ident) -> Error {
108            Error::new(repr.span(), INVALID_REPR)
109        }
110
111        let s = repr.to_string();
112        let (signed, size) = match s.as_bytes() {
113            [b'i', s @ ..] => (IntStyle::Signed, s),
114            [b'u', s @ ..] => (IntStyle::Unsigned, s),
115            _ => return Err(invalid_repr(repr)),
116        };
117        let size = match size {
118            b"8" => IntSize::_8,
119            b"16" => IntSize::_16,
120            b"32" => IntSize::_32,
121            b"64" => IntSize::_64,
122            b"128" => IntSize::_128,
123            b"size" => IntSize::Size,
124            _ => return Err(invalid_repr(repr)),
125        };
126
127        Ok(Repr { ident: repr.clone(), style: signed, size })
128    }
129}
130
131impl fmt::Display for Repr {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        fmt::Display::fmt(&self.ident, f)
134    }
135}
136
137impl ToTokens for Repr {
138    fn to_tokens(&self, tokens: &mut TokenStream) {
139        self.ident.to_tokens(tokens);
140    }
141}
142
143impl IdentFragment for Repr {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        IdentFragment::fmt(&self.ident, f)
146    }
147}
148
149impl IdentFragment for IntStyle {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match self {
152            IntStyle::Signed => write!(f, "i"),
153            IntStyle::Unsigned => write!(f, "u"),
154        }
155    }
156}
157
158impl IdentFragment for IntSize {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        match self {
161            IntSize::_8 => write!(f, "8"),
162            IntSize::_16 => write!(f, "16"),
163            IntSize::_32 => write!(f, "32"),
164            IntSize::_64 => write!(f, "64"),
165            IntSize::_128 => write!(f, "128"),
166            IntSize::Size => write!(f, "size"),
167        }
168    }
169}