1use proc_macro::TokenStream;
6use quote::quote;
7use syn::{DataEnum, DataStruct, DeriveInput};
8
9#[proc_macro_derive(Serialize)]
49pub fn impl_serialize(input: TokenStream) -> TokenStream {
50 let ast = syn::parse_macro_input!(input as DeriveInput);
51
52 match ast.data {
53 syn::Data::Struct(decl) => gen_struct_serialize(ast.ident, decl),
54 syn::Data::Enum(decl) => gen_enum_serialize(ast.ident, decl),
55 syn::Data::Union(_) => todo!(),
56 }
57}
58
59#[proc_macro_derive(Deserialize)]
139pub fn impl_deserialize(input: TokenStream) -> TokenStream {
140 let ast = syn::parse_macro_input!(input as DeriveInput);
141
142 match ast.data {
143 syn::Data::Struct(decl) => gen_struct_deserialize(ast.ident, decl),
144 syn::Data::Enum(decl) => gen_enum_deserialize(ast.ident, decl),
145 syn::Data::Union(_) => todo!(),
146 }
147}
148
149fn gen_struct_serialize(ident: syn::Ident, decl: DataStruct) -> TokenStream {
150 let c_ident = ident;
151 let n_fields = decl.fields.len();
152 let mut ser_fields = decl
153 .fields
154 .into_iter()
155 .map(|f| f.ident)
156 .filter(|f| f.is_some())
157 .map(|f| f.unwrap());
158 let closing_field = ser_fields.next_back()
159 .map(|f| Some(quote!(ser.serialize_field(stringify!(#f), &self.#f, &contra::lib_contra::position::Position::Closing )?; ))).into_iter();
160 let trailing_fields = ser_fields
161 .map(|f| Some(quote!(ser.serialize_field(stringify!(#f), &self.#f, &contra::lib_contra::position::Position::Trailing)?; ))).into_iter();
162 let ser_fields = trailing_fields
163 .chain(closing_field.into_iter())
164 .filter(|f| f.is_some());
165
166 quote!(
167 impl contra::lib_contra::serialize::Serialize for #c_ident {
168 fn serialize<S: contra::lib_contra::serialize::Serializer>(&self, ser: &mut S, _pos: &contra::lib_contra::position::Position) -> contra::lib_contra::error::SuccessResult {
169 ser.begin_struct(stringify!(#c_ident), #n_fields)?;
170
171 #(#ser_fields)*
172
173 ser.end_struct(stringify!(#c_ident))?;
174
175 Ok(())
176 }
177 }
178 ).into()
179}
180
181fn gen_enum_serialize(ident: syn::Ident, decl: DataEnum) -> TokenStream {
182 let e_ident = ident;
183 let variants = decl.variants.into_iter().map(|v| v.ident);
184
185 let ser_variants = variants
186 .clone()
187 .map(|v| quote! { #e_ident::#v => ser.serialize_str(stringify!(#v)) });
188
189 quote!(
190 impl contra::lib_contra::serialize::Serialize for #e_ident {
191 fn serialize<S: contra::lib_contra::serialize::Serializer>(&self, ser: &mut S, _pos: &contra::lib_contra::position::Position) -> contra::lib_contra::error::SuccessResult {
192 match self {
193 #(#ser_variants,)*
194 }
195 }
196 }
197 ).into()
198}
199
200fn gen_enum_deserialize(ident: syn::Ident, decl: DataEnum) -> TokenStream {
201 let e_ident = ident;
202 let variants = decl.variants.into_iter().map(|v| v.ident);
203
204 let parse_variants = variants
205 .clone()
206 .map(|v| quote! { stringify!(#v) => Ok(#e_ident::#v) });
207
208 quote! {
209 impl contra::lib_contra::deserialize::Deserialize for #e_ident {
210 fn deserialize<D: contra::lib_contra::deserialize::Deserializer>(des: D) -> Result<Self, contra::lib_contra::error::AnyError> {
211 struct EnumVisitor {}
212 impl contra::lib_contra::deserialize::Visitor for EnumVisitor {
213 type Value = #e_ident;
214
215 fn expected_a(self) -> String {
216 concat!(stringify!(#e_ident), " variant").to_string()
217 }
218
219 fn visit_str(self, v: &str) -> Result<Self::Value, contra::lib_contra::error::AnyError> {
220 match v {
221 #(#parse_variants,)*
222 err => Err(format!("invalid {} variant \"{}\"", stringify!(#e_ident), err).into())
223 }
224 }
225 }
226
227 des.deserialize_str(EnumVisitor {})
228 }
229 }
230 }.into()
231}
232
233fn gen_struct_deserialize(ident: syn::Ident, decl: DataStruct) -> TokenStream {
234 let c_ident = ident;
235 let f_idents = decl
236 .fields
237 .into_iter()
238 .map(|f| f.ident)
239 .filter(|f| f.is_some())
240 .map(|f| f.unwrap());
241
242 let field_enum_decl = f_idents.clone().map(|i| quote! { #i });
243 let field_enum_parse = f_idents
244 .clone()
245 .map(|i| quote! { stringify!(#i) => Ok(Field::#i) });
246 let tmp_field_decl = f_idents.clone().map(|i| quote! { let mut #i = None });
247 let tmp_field_parse = f_idents.clone().map(|i| {
248 quote! {
249 Field::#i => {
250 if #i.is_some() {
251 return Err(concat!("duplicate field ", stringify!(#i)).into());
252 }
253 #i = Some(map.next_value()?)
254 }
255 }
256 });
257 let tmp_field_result = f_idents
258 .clone()
259 .map(|i| quote! { let #i = #i.ok_or_else(|| concat!("missing field ", stringify!(#i)))? });
260 let tmp_field_initializer_list = f_idents.clone().map(|i| quote! { #i });
261
262 quote!(
263 impl contra::lib_contra::deserialize::Deserialize for #c_ident {
264 fn deserialize<D: contra::lib_contra::deserialize::Deserializer>(de: D) -> Result<Self, contra::lib_contra::error::AnyError> {
265 enum Field {
266 #(#field_enum_decl,)*
267 }
268 impl contra::lib_contra::deserialize::Deserialize for Field {
269 fn deserialize<D: contra::lib_contra::deserialize::Deserializer>(de: D) -> Result<Self, contra::lib_contra::error::AnyError> {
270 struct FieldVisitor {}
271 impl contra::lib_contra::deserialize::Visitor for FieldVisitor {
272 type Value = Field;
273 fn expected_a(self) -> String {
274 concat!(stringify!(#c_ident), " field").into()
275 }
276 fn visit_str(self, v: &str) -> Result<Self::Value, contra::lib_contra::error::AnyError> {
277 match v {
278 #(#field_enum_parse,)*
279 val => Err(format!("unknown \"{}\" field for {}", val, stringify!(#c_ident)).into())
280 }
281 }
282 }
283 de.deserialize_str(FieldVisitor {})
284 }
285 }
286
287 struct StructVisitor {}
288 impl contra::lib_contra::deserialize::Visitor for StructVisitor {
289 type Value = #c_ident;
290 fn expected_a(self) -> String {
291 concat!(stringify!(#c_ident), " object").into()
292 }
293 fn visit_map<M: contra::lib_contra::deserialize::MapAccess>(self, mut map: M) -> Result<Self::Value, contra::lib_contra::error::AnyError> {
294 #(#tmp_field_decl;)*
295
296 while let Some(key) = map.next_key::<Field>()? {
297 match key {
298 #(#tmp_field_parse,)*
299 }
300 }
301
302 #(#tmp_field_result;)*
303
304 Ok(#c_ident {
305 #(#tmp_field_initializer_list,)*
306 })
307 }
308 }
309
310 de.deserialize_struct(StructVisitor {})
311 }
312 }
313 ).into()
314}