1 | use attr; |
2 | use proc_macro2; |
3 | use syn; |
4 | use syn::spanned::Spanned as SynSpanned; |
5 | |
6 | #[derive (Debug)] |
7 | pub struct Input<'a> { |
8 | pub attrs: attr::Input, |
9 | pub body: Body<'a>, |
10 | pub generics: &'a syn::Generics, |
11 | pub ident: syn::Ident, |
12 | pub span: proc_macro2::Span, |
13 | } |
14 | |
15 | #[derive (Debug)] |
16 | pub enum Body<'a> { |
17 | Enum(Vec<Variant<'a>>), |
18 | Struct(Style, Vec<Field<'a>>), |
19 | } |
20 | |
21 | #[derive (Debug)] |
22 | pub struct Variant<'a> { |
23 | pub attrs: attr::Input, |
24 | pub fields: Vec<Field<'a>>, |
25 | pub ident: syn::Ident, |
26 | pub style: Style, |
27 | } |
28 | |
29 | #[derive (Debug)] |
30 | pub struct Field<'a> { |
31 | pub attrs: attr::Field, |
32 | pub ident: Option<syn::Ident>, |
33 | pub ty: &'a syn::Type, |
34 | pub span: proc_macro2::Span, |
35 | } |
36 | |
37 | #[derive (Clone, Copy, Debug)] |
38 | pub enum Style { |
39 | Struct, |
40 | Tuple, |
41 | Unit, |
42 | } |
43 | |
44 | impl<'a> Input<'a> { |
45 | pub fn from_ast( |
46 | item: &'a syn::DeriveInput, |
47 | errors: &mut proc_macro2::TokenStream, |
48 | ) -> Result<Input<'a>, ()> { |
49 | let attrs = attr::Input::from_ast(&item.attrs, errors)?; |
50 | |
51 | let body = match item.data { |
52 | syn::Data::Enum(syn::DataEnum { ref variants, .. }) => { |
53 | Body::Enum(enum_from_ast(variants, errors)?) |
54 | } |
55 | syn::Data::Struct(syn::DataStruct { ref fields, .. }) => { |
56 | let (style, fields) = struct_from_ast(fields, errors)?; |
57 | Body::Struct(style, fields) |
58 | } |
59 | syn::Data::Union(..) => { |
60 | errors.extend( |
61 | syn::Error::new_spanned(item, "derivative does not support unions" ) |
62 | .to_compile_error(), |
63 | ); |
64 | return Err(()); |
65 | } |
66 | }; |
67 | |
68 | Ok(Input { |
69 | attrs, |
70 | body, |
71 | generics: &item.generics, |
72 | ident: item.ident.clone(), |
73 | span: item.span(), |
74 | }) |
75 | } |
76 | |
77 | /// Checks whether this type is an enum with only unit variants. |
78 | pub fn is_trivial_enum(&self) -> bool { |
79 | match &self.body { |
80 | Body::Enum(e) => e.iter().all(|v| v.is_unit()), |
81 | Body::Struct(..) => false, |
82 | } |
83 | } |
84 | } |
85 | |
86 | impl<'a> Body<'a> { |
87 | pub fn all_fields(&self) -> Vec<&Field> { |
88 | match *self { |
89 | Body::Enum(ref variants: &Vec>) => variantsimpl Iterator- >
|
90 | .iter() |
91 | .flat_map(|variant: &Variant<'_>| variant.fields.iter()) |
92 | .collect(), |
93 | Body::Struct(_, ref fields: &Vec>) => fields.iter().collect(), |
94 | } |
95 | } |
96 | |
97 | pub fn is_empty(&self) -> bool { |
98 | match *self { |
99 | Body::Enum(ref variants: &Vec>) => variants.is_empty(), |
100 | Body::Struct(_, ref fields: &Vec>) => fields.is_empty(), |
101 | } |
102 | } |
103 | } |
104 | |
105 | impl<'a> Variant<'a> { |
106 | /// Checks whether this variant is a unit variant. |
107 | pub fn is_unit(&self) -> bool { |
108 | self.fields.is_empty() |
109 | } |
110 | } |
111 | |
112 | fn enum_from_ast<'a>( |
113 | variants: &'a syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>, |
114 | errors: &mut proc_macro2::TokenStream, |
115 | ) -> Result<Vec<Variant<'a>>, ()> { |
116 | variantsimpl Iterator- >
|
117 | .iter() |
118 | .map(|variant: &Variant| { |
119 | let (style: Style, fields: Vec>) = struct_from_ast(&variant.fields, errors)?; |
120 | Ok(Variant { |
121 | attrs: attr::Input::from_ast(&variant.attrs, errors)?, |
122 | fields, |
123 | ident: variant.ident.clone(), |
124 | style, |
125 | }) |
126 | }) |
127 | .collect() |
128 | } |
129 | |
130 | fn struct_from_ast<'a>( |
131 | fields: &'a syn::Fields, |
132 | errors: &mut proc_macro2::TokenStream, |
133 | ) -> Result<(Style, Vec<Field<'a>>), ()> { |
134 | match *fields { |
135 | syn::Fields::Named(ref fields: &FieldsNamed) => { |
136 | Ok((Style::Struct, fields_from_ast(&fields.named, errors)?)) |
137 | } |
138 | syn::Fields::Unnamed(ref fields: &FieldsUnnamed) => { |
139 | Ok((Style::Tuple, fields_from_ast(&fields.unnamed, errors)?)) |
140 | } |
141 | syn::Fields::Unit => Ok((Style::Unit, Vec::new())), |
142 | } |
143 | } |
144 | |
145 | fn fields_from_ast<'a>( |
146 | fields: &'a syn::punctuated::Punctuated<syn::Field, syn::token::Comma>, |
147 | errors: &mut proc_macro2::TokenStream, |
148 | ) -> Result<Vec<Field<'a>>, ()> { |
149 | fieldsimpl Iterator- >
|
150 | .iter() |
151 | .map(|field: &Field| { |
152 | Ok(Field { |
153 | attrs: attr::Field::from_ast(field, errors)?, |
154 | ident: field.ident.clone(), |
155 | ty: &field.ty, |
156 | span: field.span(), |
157 | }) |
158 | }) |
159 | .collect() |
160 | } |
161 | |