derivative/
ast.rs

1use attr;
2use proc_macro2;
3use syn;
4use syn::spanned::Spanned as SynSpanned;
5
6#[derive(Debug)]
7pub 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)]
16pub enum Body<'a> {
17    Enum(Vec<Variant<'a>>),
18    Struct(Style, Vec<Field<'a>>),
19}
20
21#[derive(Debug)]
22pub 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)]
30pub 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)]
38pub enum Style {
39    Struct,
40    Tuple,
41    Unit,
42}
43
44impl<'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
86impl<'a> Body<'a> {
87    pub fn all_fields(&self) -> Vec<&Field> {
88        match *self {
89            Body::Enum(ref variants) => variants
90                .iter()
91                .flat_map(|variant| variant.fields.iter())
92                .collect(),
93            Body::Struct(_, ref fields) => fields.iter().collect(),
94        }
95    }
96
97    pub fn is_empty(&self) -> bool {
98        match *self {
99            Body::Enum(ref variants) => variants.is_empty(),
100            Body::Struct(_, ref fields) => fields.is_empty(),
101        }
102    }
103}
104
105impl<'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
112fn 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    variants
117        .iter()
118        .map(|variant| {
119            let (style, fields) = 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
130fn 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) => {
136            Ok((Style::Struct, fields_from_ast(&fields.named, errors)?))
137        }
138        syn::Fields::Unnamed(ref fields) => {
139            Ok((Style::Tuple, fields_from_ast(&fields.unnamed, errors)?))
140        }
141        syn::Fields::Unit => Ok((Style::Unit, Vec::new())),
142    }
143}
144
145fn 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    fields
150        .iter()
151        .map(|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}