serde_derive/internals/
ast.rs

1//! A Serde ast, parsed from the Syn ast and ready to generate Rust code.
2
3use internals::attr;
4use internals::check;
5use internals::{Ctxt, Derive};
6use syn;
7use syn::punctuated::Punctuated;
8
9/// A source data structure annotated with `#[derive(Serialize)]` and/or `#[derive(Deserialize)]`,
10/// parsed into an internal representation.
11pub struct Container<'a> {
12    /// The struct or enum name (without generics).
13    pub ident: syn::Ident,
14    /// Attributes on the structure, parsed for Serde.
15    pub attrs: attr::Container,
16    /// The contents of the struct or enum.
17    pub data: Data<'a>,
18    /// Any generics on the struct or enum.
19    pub generics: &'a syn::Generics,
20    /// Original input.
21    pub original: &'a syn::DeriveInput,
22}
23
24/// The fields of a struct or enum.
25///
26/// Analagous to `syn::Data`.
27pub enum Data<'a> {
28    Enum(Vec<Variant<'a>>),
29    Struct(Style, Vec<Field<'a>>),
30}
31
32/// A variant of an enum.
33pub struct Variant<'a> {
34    pub ident: syn::Ident,
35    pub attrs: attr::Variant,
36    pub style: Style,
37    pub fields: Vec<Field<'a>>,
38    pub original: &'a syn::Variant,
39}
40
41/// A field of a struct.
42pub struct Field<'a> {
43    pub member: syn::Member,
44    pub attrs: attr::Field,
45    pub ty: &'a syn::Type,
46    pub original: &'a syn::Field,
47}
48
49#[derive(Copy, Clone)]
50pub enum Style {
51    /// Named fields.
52    Struct,
53    /// Many unnamed fields.
54    Tuple,
55    /// One unnamed field.
56    Newtype,
57    /// No fields.
58    Unit,
59}
60
61impl<'a> Container<'a> {
62    /// Convert the raw Syn ast into a parsed container object, collecting errors in `cx`.
63    pub fn from_ast(
64        cx: &Ctxt,
65        item: &'a syn::DeriveInput,
66        derive: Derive,
67    ) -> Option<Container<'a>> {
68        let mut attrs = attr::Container::from_ast(cx, item);
69
70        let mut data = match &item.data {
71            syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())),
72            syn::Data::Struct(data) => {
73                let (style, fields) = struct_from_ast(cx, &data.fields, None, attrs.default());
74                Data::Struct(style, fields)
75            }
76            syn::Data::Union(_) => {
77                cx.error_spanned_by(item, "Serde does not support derive for unions");
78                return None;
79            }
80        };
81
82        let mut has_flatten = false;
83        match &mut data {
84            Data::Enum(variants) => {
85                for variant in variants {
86                    variant.attrs.rename_by_rules(attrs.rename_all_rules());
87                    for field in &mut variant.fields {
88                        if field.attrs.flatten() {
89                            has_flatten = true;
90                        }
91                        field
92                            .attrs
93                            .rename_by_rules(variant.attrs.rename_all_rules());
94                    }
95                }
96            }
97            Data::Struct(_, fields) => {
98                for field in fields {
99                    if field.attrs.flatten() {
100                        has_flatten = true;
101                    }
102                    field.attrs.rename_by_rules(attrs.rename_all_rules());
103                }
104            }
105        }
106
107        if has_flatten {
108            attrs.mark_has_flatten();
109        }
110
111        let mut item = Container {
112            ident: item.ident.clone(),
113            attrs: attrs,
114            data: data,
115            generics: &item.generics,
116            original: item,
117        };
118        check::check(cx, &mut item, derive);
119        Some(item)
120    }
121}
122
123impl<'a> Data<'a> {
124    pub fn all_fields(&'a self) -> Box<Iterator<Item = &'a Field<'a>> + 'a> {
125        match self {
126            Data::Enum(variants) => {
127                Box::new(variants.iter().flat_map(|variant| variant.fields.iter()))
128            }
129            Data::Struct(_, fields) => Box::new(fields.iter()),
130        }
131    }
132
133    pub fn has_getter(&self) -> bool {
134        self.all_fields().any(|f| f.attrs.getter().is_some())
135    }
136}
137
138fn enum_from_ast<'a>(
139    cx: &Ctxt,
140    variants: &'a Punctuated<syn::Variant, Token![,]>,
141    container_default: &attr::Default,
142) -> Vec<Variant<'a>> {
143    variants
144        .iter()
145        .map(|variant| {
146            let attrs = attr::Variant::from_ast(cx, variant);
147            let (style, fields) =
148                struct_from_ast(cx, &variant.fields, Some(&attrs), container_default);
149            Variant {
150                ident: variant.ident.clone(),
151                attrs: attrs,
152                style: style,
153                fields: fields,
154                original: variant,
155            }
156        })
157        .collect()
158}
159
160fn struct_from_ast<'a>(
161    cx: &Ctxt,
162    fields: &'a syn::Fields,
163    attrs: Option<&attr::Variant>,
164    container_default: &attr::Default,
165) -> (Style, Vec<Field<'a>>) {
166    match fields {
167        syn::Fields::Named(fields) => (
168            Style::Struct,
169            fields_from_ast(cx, &fields.named, attrs, container_default),
170        ),
171        syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => (
172            Style::Newtype,
173            fields_from_ast(cx, &fields.unnamed, attrs, container_default),
174        ),
175        syn::Fields::Unnamed(fields) => (
176            Style::Tuple,
177            fields_from_ast(cx, &fields.unnamed, attrs, container_default),
178        ),
179        syn::Fields::Unit => (Style::Unit, Vec::new()),
180    }
181}
182
183fn fields_from_ast<'a>(
184    cx: &Ctxt,
185    fields: &'a Punctuated<syn::Field, Token![,]>,
186    attrs: Option<&attr::Variant>,
187    container_default: &attr::Default,
188) -> Vec<Field<'a>> {
189    fields
190        .iter()
191        .enumerate()
192        .map(|(i, field)| Field {
193            member: match &field.ident {
194                Some(ident) => syn::Member::Named(ident.clone()),
195                None => syn::Member::Unnamed(i.into()),
196            },
197            attrs: attr::Field::from_ast(cx, i, field, attrs, container_default),
198            ty: &field.ty,
199            original: field,
200        })
201        .collect()
202}