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 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 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}