1use crate::internals::{attr, check, Ctxt, Derive};
4use proc_macro2::Ident;
5use syn::punctuated::Punctuated;
6use syn::Token;
7
8pub struct Container<'a> {
11 pub ident: syn::Ident,
13 pub attrs: attr::Container,
15 pub data: Data<'a>,
17 pub generics: &'a syn::Generics,
19 pub original: &'a syn::DeriveInput,
21}
22
23pub enum Data<'a> {
27 Enum(Vec<Variant<'a>>),
28 Struct(Style, Vec<Field<'a>>),
29}
30
31pub struct Variant<'a> {
33 pub ident: syn::Ident,
34 pub attrs: attr::Variant,
35 pub style: Style,
36 pub fields: Vec<Field<'a>>,
37 pub original: &'a syn::Variant,
38}
39
40pub struct Field<'a> {
42 pub member: syn::Member,
43 pub attrs: attr::Field,
44 pub ty: &'a syn::Type,
45 pub original: &'a syn::Field,
46}
47
48#[derive(Copy, Clone)]
49pub enum Style {
50 Struct,
52 Tuple,
54 Newtype,
56 Unit,
58}
59
60impl<'a> Container<'a> {
61 pub fn from_ast(
63 cx: &Ctxt,
64 item: &'a syn::DeriveInput,
65 derive: Derive,
66 private: &Ident,
67 ) -> Option<Container<'a>> {
68 let attrs = attr::Container::from_ast(cx, item);
69
70 let mut data = match &item.data {
71 syn::Data::Enum(data) => {
72 Data::Enum(enum_from_ast(cx, &data.variants, attrs.default(), private))
73 }
74 syn::Data::Struct(data) => {
75 let (style, fields) =
76 struct_from_ast(cx, &data.fields, None, attrs.default(), private);
77 Data::Struct(style, fields)
78 }
79 syn::Data::Union(_) => {
80 cx.error_spanned_by(item, "Serde does not support derive for unions");
81 return None;
82 }
83 };
84
85 match &mut data {
86 Data::Enum(variants) => {
87 for variant in variants {
88 variant.attrs.rename_by_rules(attrs.rename_all_rules());
89 for field in &mut variant.fields {
90 field.attrs.rename_by_rules(
91 variant
92 .attrs
93 .rename_all_rules()
94 .or(attrs.rename_all_fields_rules()),
95 );
96 }
97 }
98 }
99 Data::Struct(_, fields) => {
100 for field in fields {
101 field.attrs.rename_by_rules(attrs.rename_all_rules());
102 }
103 }
104 }
105
106 let mut item = Container {
107 ident: item.ident.clone(),
108 attrs,
109 data,
110 generics: &item.generics,
111 original: item,
112 };
113 check::check(cx, &mut item, derive);
114 Some(item)
115 }
116}
117
118impl<'a> Data<'a> {
119 pub fn all_fields(&'a self) -> Box<dyn Iterator<Item = &'a Field<'a>> + 'a> {
120 match self {
121 Data::Enum(variants) => {
122 Box::new(variants.iter().flat_map(|variant| variant.fields.iter()))
123 }
124 Data::Struct(_, fields) => Box::new(fields.iter()),
125 }
126 }
127
128 pub fn has_getter(&self) -> bool {
129 self.all_fields().any(|f| f.attrs.getter().is_some())
130 }
131}
132
133fn enum_from_ast<'a>(
134 cx: &Ctxt,
135 variants: &'a Punctuated<syn::Variant, Token![,]>,
136 container_default: &attr::Default,
137 private: &Ident,
138) -> Vec<Variant<'a>> {
139 let variants: Vec<Variant> = variants
140 .iter()
141 .map(|variant| {
142 let attrs = attr::Variant::from_ast(cx, variant);
143 let (style, fields) = struct_from_ast(
144 cx,
145 &variant.fields,
146 Some(&attrs),
147 container_default,
148 private,
149 );
150 Variant {
151 ident: variant.ident.clone(),
152 attrs,
153 style,
154 fields,
155 original: variant,
156 }
157 })
158 .collect();
159
160 let index_of_last_tagged_variant = variants
161 .iter()
162 .rposition(|variant| !variant.attrs.untagged());
163 if let Some(index_of_last_tagged_variant) = index_of_last_tagged_variant {
164 for variant in &variants[..index_of_last_tagged_variant] {
165 if variant.attrs.untagged() {
166 cx.error_spanned_by(&variant.ident, "all variants with the #[serde(untagged)] attribute must be placed at the end of the enum");
167 }
168 }
169 }
170
171 variants
172}
173
174fn struct_from_ast<'a>(
175 cx: &Ctxt,
176 fields: &'a syn::Fields,
177 attrs: Option<&attr::Variant>,
178 container_default: &attr::Default,
179 private: &Ident,
180) -> (Style, Vec<Field<'a>>) {
181 match fields {
182 syn::Fields::Named(fields) => (
183 Style::Struct,
184 fields_from_ast(cx, &fields.named, attrs, container_default, private),
185 ),
186 syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => (
187 Style::Newtype,
188 fields_from_ast(cx, &fields.unnamed, attrs, container_default, private),
189 ),
190 syn::Fields::Unnamed(fields) => (
191 Style::Tuple,
192 fields_from_ast(cx, &fields.unnamed, attrs, container_default, private),
193 ),
194 syn::Fields::Unit => (Style::Unit, Vec::new()),
195 }
196}
197
198fn fields_from_ast<'a>(
199 cx: &Ctxt,
200 fields: &'a Punctuated<syn::Field, Token![,]>,
201 attrs: Option<&attr::Variant>,
202 container_default: &attr::Default,
203 private: &Ident,
204) -> Vec<Field<'a>> {
205 fields
206 .iter()
207 .enumerate()
208 .map(|(i, field)| Field {
209 member: match &field.ident {
210 Some(ident) => syn::Member::Named(ident.clone()),
211 None => syn::Member::Unnamed(i.into()),
212 },
213 attrs: attr::Field::from_ast(cx, i, field, attrs, container_default, private),
214 ty: &field.ty,
215 original: field,
216 })
217 .collect()
218}