Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up
Find file
Copy path
Fetching contributors…
| // Copyright 2017 Serde Developers | |
| // | |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
| // option. This file may not be copied, modified, or distributed | |
| // except according to those terms. | |
| use proc_macro2::{Span, TokenStream}; | |
| use syn::spanned::Spanned; | |
| use syn::{self, Ident, Index, Member}; | |
| use bound; | |
| use fragment::{Fragment, Match, Stmts}; | |
| use internals::ast::{Container, Data, Field, Style, Variant}; | |
| use internals::{attr, Ctxt, Derive}; | |
| use pretend; | |
| use try; | |
| pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, String> { | |
| let ctxt = Ctxt::new(); | |
| let cont = Container::from_ast(&ctxt, input, Derive::Serialize); | |
| precondition(&ctxt, &cont); | |
| try!(ctxt.check()); | |
| let ident = &cont.ident; | |
| let params = Parameters::new(&cont); | |
| let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl(); | |
| let dummy_const = Ident::new(&format!("_IMPL_SERIALIZE_FOR_{}", ident), Span::call_site()); | |
| let body = Stmts(serialize_body(&cont, ¶ms)); | |
| let impl_block = if let Some(remote) = cont.attrs.remote() { | |
| let vis = &input.vis; | |
| let used = pretend::pretend_used(&cont); | |
| quote! { | |
| impl #impl_generics #ident #ty_generics #where_clause { | |
| #vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error> | |
| where | |
| __S: _serde::Serializer, | |
| { | |
| #used | |
| #body | |
| } | |
| } | |
| } | |
| } else { | |
| quote! { | |
| #[automatically_derived] | |
| impl #impl_generics _serde::Serialize for #ident #ty_generics #where_clause { | |
| fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error> | |
| where | |
| __S: _serde::Serializer, | |
| { | |
| #body | |
| } | |
| } | |
| } | |
| }; | |
| let try_replacement = try::replacement(); | |
| let generated = quote! { | |
| #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] | |
| const #dummy_const: () = { | |
| extern crate serde as _serde; | |
| #try_replacement | |
| #impl_block | |
| }; | |
| }; | |
| Ok(generated) | |
| } | |
| fn precondition(cx: &Ctxt, cont: &Container) { | |
| match cont.attrs.identifier() { | |
| attr::Identifier::No => {} | |
| attr::Identifier::Field => { | |
| cx.error("field identifiers cannot be serialized"); | |
| } | |
| attr::Identifier::Variant => { | |
| cx.error("variant identifiers cannot be serialized"); | |
| } | |
| } | |
| } | |
| struct Parameters { | |
| /// Variable holding the value being serialized. Either `self` for local | |
| /// types or `__self` for remote types. | |
| self_var: Ident, | |
| /// Path to the type the impl is for. Either a single `Ident` for local | |
| /// types or `some::remote::Ident` for remote types. Does not include | |
| /// generic parameters. | |
| this: syn::Path, | |
| /// Generics including any explicit and inferred bounds for the impl. | |
| generics: syn::Generics, | |
| /// Type has a `serde(remote = "...")` attribute. | |
| is_remote: bool, | |
| } | |
| impl Parameters { | |
| fn new(cont: &Container) -> Self { | |
| let is_remote = cont.attrs.remote().is_some(); | |
| let self_var = if is_remote { | |
| Ident::new("__self", Span::call_site()) | |
| } else { | |
| Ident::new("self", Span::call_site()) | |
| }; | |
| let this = match cont.attrs.remote() { | |
| Some(remote) => remote.clone(), | |
| None => cont.ident.clone().into(), | |
| }; | |
| let generics = build_generics(cont); | |
| Parameters { | |
| self_var: self_var, | |
| this: this, | |
| generics: generics, | |
| is_remote: is_remote, | |
| } | |
| } | |
| /// Type name to use in error messages and `&'static str` arguments to | |
| /// various Serializer methods. | |
| fn type_name(&self) -> String { | |
| self.this.segments.last().unwrap().value().ident.to_string() | |
| } | |
| } | |
| // All the generics in the input, plus a bound `T: Serialize` for each generic | |
| // field type that will be serialized by us. | |
| fn build_generics(cont: &Container) -> syn::Generics { | |
| let generics = bound::without_defaults(cont.generics); | |
| let generics = | |
| bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound); | |
| let generics = | |
| bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::ser_bound); | |
| match cont.attrs.ser_bound() { | |
| Some(predicates) => bound::with_where_predicates(&generics, predicates), | |
| None => bound::with_bound( | |
| cont, | |
| &generics, | |
| needs_serialize_bound, | |
| &parse_quote!(_serde::Serialize), | |
| ), | |
| } | |
| } | |
| // Fields with a `skip_serializing` or `serialize_with` attribute, or which | |
| // belong to a variant with a 'skip_serializing` or `serialize_with` attribute, | |
| // are not serialized by us so we do not generate a bound. Fields with a `bound` | |
| // attribute specify their own bound so we do not generate one. All other fields | |
| // may need a `T: Serialize` bound where T is the type of the field. | |
| fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool { | |
| !field.skip_serializing() && field.serialize_with().is_none() && field.ser_bound().is_none() | |
| && variant.map_or(true, |variant| { | |
| !variant.skip_serializing() | |
| && variant.serialize_with().is_none() | |
| && variant.ser_bound().is_none() | |
| }) | |
| } | |
| fn serialize_body(cont: &Container, params: &Parameters) -> Fragment { | |
| if cont.attrs.transparent() { | |
| serialize_transparent(cont, params) | |
| } else if let Some(type_into) = cont.attrs.type_into() { | |
| serialize_into(params, type_into) | |
| } else { | |
| match cont.data { | |
| Data::Enum(ref variants) => serialize_enum(params, variants, &cont.attrs), | |
| Data::Struct(Style::Struct, ref fields) => { | |
| serialize_struct(params, fields, &cont.attrs) | |
| } | |
| Data::Struct(Style::Tuple, ref fields) => { | |
| serialize_tuple_struct(params, fields, &cont.attrs) | |
| } | |
| Data::Struct(Style::Newtype, ref fields) => { | |
| serialize_newtype_struct(params, &fields[0], &cont.attrs) | |
| } | |
| Data::Struct(Style::Unit, _) => serialize_unit_struct(&cont.attrs), | |
| } | |
| } | |
| } | |
| fn serialize_transparent(cont: &Container, params: &Parameters) -> Fragment { | |
| let fields = match cont.data { | |
| Data::Struct(_, ref fields) => fields, | |
| Data::Enum(_) => unreachable!(), | |
| }; | |
| let self_var = ¶ms.self_var; | |
| let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap(); | |
| let member = &transparent_field.member; | |
| let path = match transparent_field.attrs.serialize_with() { | |
| Some(path) => quote!(#path), | |
| None => quote!(_serde::Serialize::serialize), | |
| }; | |
| quote_block! { | |
| #path(&#self_var.#member, __serializer) | |
| } | |
| } | |
| fn serialize_into(params: &Parameters, type_into: &syn::Type) -> Fragment { | |
| let self_var = ¶ms.self_var; | |
| quote_block! { | |
| _serde::Serialize::serialize( | |
| &_serde::export::Into::<#type_into>::into(_serde::export::Clone::clone(#self_var)), | |
| __serializer) | |
| } | |
| } | |
| fn serialize_unit_struct(cattrs: &attr::Container) -> Fragment { | |
| let type_name = cattrs.name().serialize_name(); | |
| quote_expr! { | |
| _serde::Serializer::serialize_unit_struct(__serializer, #type_name) | |
| } | |
| } | |
| fn serialize_newtype_struct( | |
| params: &Parameters, | |
| field: &Field, | |
| cattrs: &attr::Container, | |
| ) -> Fragment { | |
| let type_name = cattrs.name().serialize_name(); | |
| let mut field_expr = get_member( | |
| params, | |
| field, | |
| &Member::Unnamed(Index { | |
| index: 0, | |
| span: Span::call_site(), | |
| }), | |
| ); | |
| if let Some(path) = field.attrs.serialize_with() { | |
| field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); | |
| } | |
| let span = field.original.span(); | |
| let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_struct); | |
| quote_expr! { | |
| #func(__serializer, #type_name, #field_expr) | |
| } | |
| } | |
| fn serialize_tuple_struct( | |
| params: &Parameters, | |
| fields: &[Field], | |
| cattrs: &attr::Container, | |
| ) -> Fragment { | |
| let serialize_stmts = | |
| serialize_tuple_struct_visitor(fields, params, false, &TupleTrait::SerializeTupleStruct); | |
| let type_name = cattrs.name().serialize_name(); | |
| let mut serialized_fields = fields | |
| .iter() | |
| .enumerate() | |
| .filter(|&(_, ref field)| !field.attrs.skip_serializing()) | |
| .peekable(); | |
| let let_mut = mut_if(serialized_fields.peek().is_some()); | |
| let len = serialized_fields | |
| .map(|(i, field)| match field.attrs.skip_serializing_if() { | |
| None => quote!(1), | |
| Some(path) => { | |
| let index = syn::Index { | |
| index: i as u32, | |
| span: Span::call_site(), | |
| }; | |
| let field_expr = get_member(params, field, &Member::Unnamed(index)); | |
| quote!(if #path(#field_expr) { 0 } else { 1 }) | |
| } | |
| }) | |
| .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_struct(__serializer, #type_name, #len)); | |
| #(#serialize_stmts)* | |
| _serde::ser::SerializeTupleStruct::end(__serde_state) | |
| } | |
| } | |
| fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment { | |
| assert!(fields.len() as u64 <= u64::from(u32::max_value())); | |
| if cattrs.has_flatten() { | |
| serialize_struct_as_map(params, fields, cattrs) | |
| } else { | |
| serialize_struct_as_struct(params, fields, cattrs) | |
| } | |
| } | |
| fn serialize_struct_as_struct( | |
| params: &Parameters, | |
| fields: &[Field], | |
| cattrs: &attr::Container, | |
| ) -> Fragment { | |
| let serialize_fields = | |
| serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct); | |
| let type_name = cattrs.name().serialize_name(); | |
| let mut serialized_fields = fields | |
| .iter() | |
| .filter(|&field| !field.attrs.skip_serializing()) | |
| .peekable(); | |
| let let_mut = mut_if(serialized_fields.peek().is_some()); | |
| let len = serialized_fields | |
| .map(|field| match field.attrs.skip_serializing_if() { | |
| None => quote!(1), | |
| Some(path) => { | |
| let field_expr = get_member(params, field, &field.member); | |
| quote!(if #path(#field_expr) { 0 } else { 1 }) | |
| } | |
| }) | |
| .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len)); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeStruct::end(__serde_state) | |
| } | |
| } | |
| fn serialize_struct_as_map( | |
| params: &Parameters, | |
| fields: &[Field], | |
| cattrs: &attr::Container, | |
| ) -> Fragment { | |
| let serialize_fields = | |
| serialize_struct_visitor(fields, params, false, &StructTrait::SerializeMap); | |
| let mut serialized_fields = fields | |
| .iter() | |
| .filter(|&field| !field.attrs.skip_serializing()) | |
| .peekable(); | |
| let let_mut = mut_if(serialized_fields.peek().is_some()); | |
| let len = if cattrs.has_flatten() { | |
| quote!(_serde::export::None) | |
| } else { | |
| let len = serialized_fields | |
| .map(|field| match field.attrs.skip_serializing_if() { | |
| None => quote!(1), | |
| Some(path) => { | |
| let field_expr = get_member(params, field, &field.member); | |
| quote!(if #path(#field_expr) { 0 } else { 1 }) | |
| } | |
| }) | |
| .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); | |
| quote!(_serde::export::Some(#len)) | |
| }; | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(__serializer, #len)); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeMap::end(__serde_state) | |
| } | |
| } | |
| fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment { | |
| assert!(variants.len() as u64 <= u64::from(u32::max_value())); | |
| let self_var = ¶ms.self_var; | |
| let arms: Vec<_> = variants | |
| .iter() | |
| .enumerate() | |
| .map(|(variant_index, variant)| { | |
| serialize_variant(params, variant, variant_index as u32, cattrs) | |
| }) | |
| .collect(); | |
| quote_expr! { | |
| match *#self_var { | |
| #(#arms)* | |
| } | |
| } | |
| } | |
| fn serialize_variant( | |
| params: &Parameters, | |
| variant: &Variant, | |
| variant_index: u32, | |
| cattrs: &attr::Container, | |
| ) -> TokenStream { | |
| let this = ¶ms.this; | |
| let variant_ident = &variant.ident; | |
| if variant.attrs.skip_serializing() { | |
| let skipped_msg = format!( | |
| "the enum variant {}::{} cannot be serialized", | |
| params.type_name(), | |
| variant_ident | |
| ); | |
| let skipped_err = quote! { | |
| _serde::export::Err(_serde::ser::Error::custom(#skipped_msg)) | |
| }; | |
| let fields_pat = match variant.style { | |
| Style::Unit => quote!(), | |
| Style::Newtype | Style::Tuple => quote!((..)), | |
| Style::Struct => quote!({ .. }), | |
| }; | |
| quote! { | |
| #this::#variant_ident #fields_pat => #skipped_err, | |
| } | |
| } else { | |
| // variant wasn't skipped | |
| let case = match variant.style { | |
| Style::Unit => { | |
| quote! { | |
| #this::#variant_ident | |
| } | |
| } | |
| Style::Newtype => { | |
| quote! { | |
| #this::#variant_ident(ref __field0) | |
| } | |
| } | |
| Style::Tuple => { | |
| let field_names = (0..variant.fields.len()) | |
| .map(|i| Ident::new(&format!("__field{}", i), Span::call_site())); | |
| quote! { | |
| #this::#variant_ident(#(ref #field_names),*) | |
| } | |
| } | |
| Style::Struct => { | |
| let members = variant.fields.iter().map(|f| &f.member); | |
| quote! { | |
| #this::#variant_ident { #(ref #members),* } | |
| } | |
| } | |
| }; | |
| let body = Match(match *cattrs.tag() { | |
| attr::EnumTag::External => { | |
| serialize_externally_tagged_variant(params, variant, variant_index, cattrs) | |
| } | |
| attr::EnumTag::Internal { ref tag } => { | |
| serialize_internally_tagged_variant(params, variant, cattrs, tag) | |
| } | |
| attr::EnumTag::Adjacent { | |
| ref tag, | |
| ref content, | |
| } => serialize_adjacently_tagged_variant(params, variant, cattrs, tag, content), | |
| attr::EnumTag::None => serialize_untagged_variant(params, variant, cattrs), | |
| }); | |
| quote! { | |
| #case => #body | |
| } | |
| } | |
| } | |
| fn serialize_externally_tagged_variant( | |
| params: &Parameters, | |
| variant: &Variant, | |
| variant_index: u32, | |
| cattrs: &attr::Container, | |
| ) -> Fragment { | |
| let type_name = cattrs.name().serialize_name(); | |
| let variant_name = variant.attrs.name().serialize_name(); | |
| if let Some(path) = variant.attrs.serialize_with() { | |
| let ser = wrap_serialize_variant_with(params, path, variant); | |
| return quote_expr! { | |
| _serde::Serializer::serialize_newtype_variant( | |
| __serializer, | |
| #type_name, | |
| #variant_index, | |
| #variant_name, | |
| #ser, | |
| ) | |
| }; | |
| } | |
| match variant.style { | |
| Style::Unit => { | |
| quote_expr! { | |
| _serde::Serializer::serialize_unit_variant( | |
| __serializer, | |
| #type_name, | |
| #variant_index, | |
| #variant_name, | |
| ) | |
| } | |
| } | |
| Style::Newtype => { | |
| let field = &variant.fields[0]; | |
| let mut field_expr = quote!(__field0); | |
| if let Some(path) = field.attrs.serialize_with() { | |
| field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); | |
| } | |
| quote_expr! { | |
| _serde::Serializer::serialize_newtype_variant( | |
| __serializer, | |
| #type_name, | |
| #variant_index, | |
| #variant_name, | |
| #field_expr, | |
| ) | |
| } | |
| } | |
| Style::Tuple => serialize_tuple_variant( | |
| TupleVariant::ExternallyTagged { | |
| type_name: type_name, | |
| variant_index: variant_index, | |
| variant_name: variant_name, | |
| }, | |
| params, | |
| &variant.fields, | |
| ), | |
| Style::Struct => serialize_struct_variant( | |
| StructVariant::ExternallyTagged { | |
| variant_index: variant_index, | |
| variant_name: variant_name, | |
| }, | |
| params, | |
| &variant.fields, | |
| &type_name, | |
| ), | |
| } | |
| } | |
| fn serialize_internally_tagged_variant( | |
| params: &Parameters, | |
| variant: &Variant, | |
| cattrs: &attr::Container, | |
| tag: &str, | |
| ) -> Fragment { | |
| let type_name = cattrs.name().serialize_name(); | |
| let variant_name = variant.attrs.name().serialize_name(); | |
| let enum_ident_str = params.type_name(); | |
| let variant_ident_str = variant.ident.to_string(); | |
| if let Some(path) = variant.attrs.serialize_with() { | |
| let ser = wrap_serialize_variant_with(params, path, variant); | |
| return quote_expr! { | |
| _serde::private::ser::serialize_tagged_newtype( | |
| __serializer, | |
| #enum_ident_str, | |
| #variant_ident_str, | |
| #tag, | |
| #variant_name, | |
| #ser, | |
| ) | |
| }; | |
| } | |
| match variant.style { | |
| Style::Unit => { | |
| quote_block! { | |
| let mut __struct = try!(_serde::Serializer::serialize_struct( | |
| __serializer, #type_name, 1)); | |
| try!(_serde::ser::SerializeStruct::serialize_field( | |
| &mut __struct, #tag, #variant_name)); | |
| _serde::ser::SerializeStruct::end(__struct) | |
| } | |
| } | |
| Style::Newtype => { | |
| let field = &variant.fields[0]; | |
| let mut field_expr = quote!(__field0); | |
| if let Some(path) = field.attrs.serialize_with() { | |
| field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); | |
| } | |
| quote_expr! { | |
| _serde::private::ser::serialize_tagged_newtype( | |
| __serializer, | |
| #enum_ident_str, | |
| #variant_ident_str, | |
| #tag, | |
| #variant_name, | |
| #field_expr, | |
| ) | |
| } | |
| } | |
| Style::Struct => serialize_struct_variant( | |
| StructVariant::InternallyTagged { | |
| tag: tag, | |
| variant_name: variant_name, | |
| }, | |
| params, | |
| &variant.fields, | |
| &type_name, | |
| ), | |
| Style::Tuple => unreachable!("checked in serde_derive_internals"), | |
| } | |
| } | |
| fn serialize_adjacently_tagged_variant( | |
| params: &Parameters, | |
| variant: &Variant, | |
| cattrs: &attr::Container, | |
| tag: &str, | |
| content: &str, | |
| ) -> Fragment { | |
| let this = ¶ms.this; | |
| let type_name = cattrs.name().serialize_name(); | |
| let variant_name = variant.attrs.name().serialize_name(); | |
| let inner = Stmts(if let Some(path) = variant.attrs.serialize_with() { | |
| let ser = wrap_serialize_variant_with(params, path, variant); | |
| quote_expr! { | |
| _serde::Serialize::serialize(#ser, __serializer) | |
| } | |
| } else { | |
| match variant.style { | |
| Style::Unit => { | |
| return quote_block! { | |
| let mut __struct = try!(_serde::Serializer::serialize_struct( | |
| __serializer, #type_name, 1)); | |
| try!(_serde::ser::SerializeStruct::serialize_field( | |
| &mut __struct, #tag, #variant_name)); | |
| _serde::ser::SerializeStruct::end(__struct) | |
| }; | |
| } | |
| Style::Newtype => { | |
| let field = &variant.fields[0]; | |
| let mut field_expr = quote!(__field0); | |
| if let Some(path) = field.attrs.serialize_with() { | |
| field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); | |
| } | |
| return quote_block! { | |
| let mut __struct = try!(_serde::Serializer::serialize_struct( | |
| __serializer, #type_name, 2)); | |
| try!(_serde::ser::SerializeStruct::serialize_field( | |
| &mut __struct, #tag, #variant_name)); | |
| try!(_serde::ser::SerializeStruct::serialize_field( | |
| &mut __struct, #content, #field_expr)); | |
| _serde::ser::SerializeStruct::end(__struct) | |
| }; | |
| } | |
| Style::Tuple => { | |
| serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields) | |
| } | |
| Style::Struct => serialize_struct_variant( | |
| StructVariant::Untagged, | |
| params, | |
| &variant.fields, | |
| &variant_name, | |
| ), | |
| } | |
| }); | |
| let fields_ty = variant.fields.iter().map(|f| &f.ty); | |
| let fields_ident: &Vec<_> = &match variant.style { | |
| Style::Unit => { | |
| if variant.attrs.serialize_with().is_some() { | |
| vec![] | |
| } else { | |
| unreachable!() | |
| } | |
| } | |
| Style::Newtype => vec![Member::Named(Ident::new("__field0", Span::call_site()))], | |
| Style::Tuple => (0..variant.fields.len()) | |
| .map(|i| Member::Named(Ident::new(&format!("__field{}", i), Span::call_site()))) | |
| .collect(), | |
| Style::Struct => variant.fields.iter().map(|f| f.member.clone()).collect(), | |
| }; | |
| let (_, ty_generics, where_clause) = params.generics.split_for_impl(); | |
| let wrapper_generics = if fields_ident.is_empty() { | |
| params.generics.clone() | |
| } else { | |
| bound::with_lifetime_bound(¶ms.generics, "'__a") | |
| }; | |
| let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl(); | |
| quote_block! { | |
| struct __AdjacentlyTagged #wrapper_generics #where_clause { | |
| data: (#(&'__a #fields_ty,)*), | |
| phantom: _serde::export::PhantomData<#this #ty_generics>, | |
| } | |
| impl #wrapper_impl_generics _serde::Serialize for __AdjacentlyTagged #wrapper_ty_generics #where_clause { | |
| fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error> | |
| where | |
| __S: _serde::Serializer, | |
| { | |
| let (#(#fields_ident,)*) = self.data; | |
| #inner | |
| } | |
| } | |
| let mut __struct = try!(_serde::Serializer::serialize_struct( | |
| __serializer, #type_name, 2)); | |
| try!(_serde::ser::SerializeStruct::serialize_field( | |
| &mut __struct, #tag, #variant_name)); | |
| try!(_serde::ser::SerializeStruct::serialize_field( | |
| &mut __struct, #content, &__AdjacentlyTagged { | |
| data: (#(#fields_ident,)*), | |
| phantom: _serde::export::PhantomData::<#this #ty_generics>, | |
| })); | |
| _serde::ser::SerializeStruct::end(__struct) | |
| } | |
| } | |
| fn serialize_untagged_variant( | |
| params: &Parameters, | |
| variant: &Variant, | |
| cattrs: &attr::Container, | |
| ) -> Fragment { | |
| if let Some(path) = variant.attrs.serialize_with() { | |
| let ser = wrap_serialize_variant_with(params, path, variant); | |
| return quote_expr! { | |
| _serde::Serialize::serialize(#ser, __serializer) | |
| }; | |
| } | |
| match variant.style { | |
| Style::Unit => { | |
| quote_expr! { | |
| _serde::Serializer::serialize_unit(__serializer) | |
| } | |
| } | |
| Style::Newtype => { | |
| let field = &variant.fields[0]; | |
| let mut field_expr = quote!(__field0); | |
| if let Some(path) = field.attrs.serialize_with() { | |
| field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); | |
| } | |
| quote_expr! { | |
| _serde::Serialize::serialize(#field_expr, __serializer) | |
| } | |
| } | |
| Style::Tuple => serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields), | |
| Style::Struct => { | |
| let type_name = cattrs.name().serialize_name(); | |
| serialize_struct_variant(StructVariant::Untagged, params, &variant.fields, &type_name) | |
| } | |
| } | |
| } | |
| enum TupleVariant { | |
| ExternallyTagged { | |
| type_name: String, | |
| variant_index: u32, | |
| variant_name: String, | |
| }, | |
| Untagged, | |
| } | |
| fn serialize_tuple_variant( | |
| context: TupleVariant, | |
| params: &Parameters, | |
| fields: &[Field], | |
| ) -> Fragment { | |
| let tuple_trait = match context { | |
| TupleVariant::ExternallyTagged { .. } => TupleTrait::SerializeTupleVariant, | |
| TupleVariant::Untagged => TupleTrait::SerializeTuple, | |
| }; | |
| let serialize_stmts = serialize_tuple_struct_visitor(fields, params, true, &tuple_trait); | |
| let mut serialized_fields = fields | |
| .iter() | |
| .enumerate() | |
| .filter(|&(_, ref field)| !field.attrs.skip_serializing()) | |
| .peekable(); | |
| let let_mut = mut_if(serialized_fields.peek().is_some()); | |
| let len = serialized_fields | |
| .map(|(i, field)| match field.attrs.skip_serializing_if() { | |
| None => quote!(1), | |
| Some(path) => { | |
| let field_expr = Ident::new(&format!("__field{}", i), Span::call_site()); | |
| quote!(if #path(#field_expr) { 0 } else { 1 }) | |
| } | |
| }) | |
| .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); | |
| match context { | |
| TupleVariant::ExternallyTagged { | |
| type_name, | |
| variant_index, | |
| variant_name, | |
| } => { | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_variant( | |
| __serializer, | |
| #type_name, | |
| #variant_index, | |
| #variant_name, | |
| #len)); | |
| #(#serialize_stmts)* | |
| _serde::ser::SerializeTupleVariant::end(__serde_state) | |
| } | |
| } | |
| TupleVariant::Untagged => { | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple( | |
| __serializer, | |
| #len)); | |
| #(#serialize_stmts)* | |
| _serde::ser::SerializeTuple::end(__serde_state) | |
| } | |
| } | |
| } | |
| } | |
| enum StructVariant<'a> { | |
| ExternallyTagged { | |
| variant_index: u32, | |
| variant_name: String, | |
| }, | |
| InternallyTagged { | |
| tag: &'a str, | |
| variant_name: String, | |
| }, | |
| Untagged, | |
| } | |
| fn serialize_struct_variant<'a>( | |
| context: StructVariant<'a>, | |
| params: &Parameters, | |
| fields: &[Field], | |
| name: &str, | |
| ) -> Fragment { | |
| if fields.iter().any(|field| field.attrs.flatten()) { | |
| return serialize_struct_variant_with_flatten(context, params, fields, name); | |
| } | |
| let struct_trait = match context { | |
| StructVariant::ExternallyTagged { .. } => (StructTrait::SerializeStructVariant), | |
| StructVariant::InternallyTagged { .. } | StructVariant::Untagged => { | |
| (StructTrait::SerializeStruct) | |
| } | |
| }; | |
| let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait); | |
| let mut serialized_fields = fields | |
| .iter() | |
| .filter(|&field| !field.attrs.skip_serializing()) | |
| .peekable(); | |
| let let_mut = mut_if(serialized_fields.peek().is_some()); | |
| let len = serialized_fields | |
| .map(|field| { | |
| let member = &field.member; | |
| match field.attrs.skip_serializing_if() { | |
| Some(path) => quote!(if #path(#member) { 0 } else { 1 }), | |
| None => quote!(1), | |
| } | |
| }) | |
| .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); | |
| match context { | |
| StructVariant::ExternallyTagged { | |
| variant_index, | |
| variant_name, | |
| } => { | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct_variant( | |
| __serializer, | |
| #name, | |
| #variant_index, | |
| #variant_name, | |
| #len, | |
| )); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeStructVariant::end(__serde_state) | |
| } | |
| } | |
| StructVariant::InternallyTagged { tag, variant_name } => { | |
| quote_block! { | |
| let mut __serde_state = try!(_serde::Serializer::serialize_struct( | |
| __serializer, | |
| #name, | |
| #len + 1, | |
| )); | |
| try!(_serde::ser::SerializeStruct::serialize_field( | |
| &mut __serde_state, | |
| #tag, | |
| #variant_name, | |
| )); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeStruct::end(__serde_state) | |
| } | |
| } | |
| StructVariant::Untagged => { | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct( | |
| __serializer, | |
| #name, | |
| #len, | |
| )); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeStruct::end(__serde_state) | |
| } | |
| } | |
| } | |
| } | |
| fn serialize_struct_variant_with_flatten<'a>( | |
| context: StructVariant<'a>, | |
| params: &Parameters, | |
| fields: &[Field], | |
| name: &str, | |
| ) -> Fragment { | |
| let struct_trait = StructTrait::SerializeMap; | |
| let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait); | |
| let mut serialized_fields = fields | |
| .iter() | |
| .filter(|&field| !field.attrs.skip_serializing()) | |
| .peekable(); | |
| let let_mut = mut_if(serialized_fields.peek().is_some()); | |
| match context { | |
| StructVariant::ExternallyTagged { | |
| variant_index, | |
| variant_name, | |
| } => { | |
| let this = ¶ms.this; | |
| let fields_ty = fields.iter().map(|f| &f.ty); | |
| let members = &fields.iter().map(|f| &f.member).collect::<Vec<_>>(); | |
| let (_, ty_generics, where_clause) = params.generics.split_for_impl(); | |
| let wrapper_generics = bound::with_lifetime_bound(¶ms.generics, "'__a"); | |
| let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl(); | |
| quote_block! { | |
| struct __EnumFlatten #wrapper_generics #where_clause { | |
| data: (#(&'__a #fields_ty,)*), | |
| phantom: _serde::export::PhantomData<#this #ty_generics>, | |
| } | |
| impl #wrapper_impl_generics _serde::Serialize for __EnumFlatten #wrapper_ty_generics #where_clause { | |
| fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error> | |
| where | |
| __S: _serde::Serializer, | |
| { | |
| let (#(#members,)*) = self.data; | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_map( | |
| __serializer, | |
| _serde::export::None)); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeMap::end(__serde_state) | |
| } | |
| } | |
| _serde::Serializer::serialize_newtype_variant( | |
| __serializer, | |
| #name, | |
| #variant_index, | |
| #variant_name, | |
| &__EnumFlatten { | |
| data: (#(#members,)*), | |
| phantom: _serde::export::PhantomData::<#this #ty_generics>, | |
| }) | |
| } | |
| } | |
| StructVariant::InternallyTagged { tag, variant_name } => { | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_map( | |
| __serializer, | |
| _serde::export::None)); | |
| try!(_serde::ser::SerializeMap::serialize_entry( | |
| &mut __serde_state, | |
| #tag, | |
| #variant_name, | |
| )); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeMap::end(__serde_state) | |
| } | |
| } | |
| StructVariant::Untagged => { | |
| quote_block! { | |
| let #let_mut __serde_state = try!(_serde::Serializer::serialize_map( | |
| __serializer, | |
| _serde::export::None)); | |
| #(#serialize_fields)* | |
| _serde::ser::SerializeMap::end(__serde_state) | |
| } | |
| } | |
| } | |
| } | |
| fn serialize_tuple_struct_visitor( | |
| fields: &[Field], | |
| params: &Parameters, | |
| is_enum: bool, | |
| tuple_trait: &TupleTrait, | |
| ) -> Vec<TokenStream> { | |
| fields | |
| .iter() | |
| .enumerate() | |
| .filter(|&(_, ref field)| !field.attrs.skip_serializing()) | |
| .map(|(i, field)| { | |
| let mut field_expr = if is_enum { | |
| let id = Ident::new(&format!("__field{}", i), Span::call_site()); | |
| quote!(#id) | |
| } else { | |
| get_member( | |
| params, | |
| field, | |
| &Member::Unnamed(Index { | |
| index: i as u32, | |
| span: Span::call_site(), | |
| }), | |
| ) | |
| }; | |
| let skip = field | |
| .attrs | |
| .skip_serializing_if() | |
| .map(|path| quote!(#path(#field_expr))); | |
| if let Some(path) = field.attrs.serialize_with() { | |
| field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); | |
| } | |
| let span = field.original.span(); | |
| let func = tuple_trait.serialize_element(span); | |
| let ser = quote! { | |
| try!(#func(&mut __serde_state, #field_expr)); | |
| }; | |
| match skip { | |
| None => ser, | |
| Some(skip) => quote!(if !#skip { #ser }), | |
| } | |
| }) | |
| .collect() | |
| } | |
| fn serialize_struct_visitor( | |
| fields: &[Field], | |
| params: &Parameters, | |
| is_enum: bool, | |
| struct_trait: &StructTrait, | |
| ) -> Vec<TokenStream> { | |
| fields | |
| .iter() | |
| .filter(|&field| !field.attrs.skip_serializing()) | |
| .map(|field| { | |
| let member = &field.member; | |
| let mut field_expr = if is_enum { | |
| quote!(#member) | |
| } else { | |
| get_member(params, field, &member) | |
| }; | |
| let key_expr = field.attrs.name().serialize_name(); | |
| let skip = field | |
| .attrs | |
| .skip_serializing_if() | |
| .map(|path| quote!(#path(#field_expr))); | |
| if let Some(path) = field.attrs.serialize_with() { | |
| field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); | |
| } | |
| let span = field.original.span(); | |
| let ser = if field.attrs.flatten() { | |
| quote! { | |
| try!(_serde::Serialize::serialize(&#field_expr, _serde::private::ser::FlatMapSerializer(&mut __serde_state))); | |
| } | |
| } else { | |
| let func = struct_trait.serialize_field(span); | |
| quote! { | |
| try!(#func(&mut __serde_state, #key_expr, #field_expr)); | |
| } | |
| }; | |
| match skip { | |
| None => ser, | |
| Some(skip) => { | |
| if let Some(skip_func) = struct_trait.skip_field(span) { | |
| quote! { | |
| if !#skip { | |
| #ser | |
| } else { | |
| try!(#skip_func(&mut __serde_state, #key_expr)); | |
| } | |
| } | |
| } else { | |
| quote! { | |
| if !#skip { | |
| #ser | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }) | |
| .collect() | |
| } | |
| fn wrap_serialize_field_with( | |
| params: &Parameters, | |
| field_ty: &syn::Type, | |
| serialize_with: &syn::ExprPath, | |
| field_expr: &TokenStream, | |
| ) -> TokenStream { | |
| wrap_serialize_with(params, serialize_with, &[field_ty], &[quote!(#field_expr)]) | |
| } | |
| fn wrap_serialize_variant_with( | |
| params: &Parameters, | |
| serialize_with: &syn::ExprPath, | |
| variant: &Variant, | |
| ) -> TokenStream { | |
| let field_tys: Vec<_> = variant.fields.iter().map(|field| field.ty).collect(); | |
| let field_exprs: Vec<_> = variant | |
| .fields | |
| .iter() | |
| .map(|field| { | |
| let id = match field.member { | |
| Member::Named(ref ident) => ident.clone(), | |
| Member::Unnamed(ref member) => { | |
| Ident::new(&format!("__field{}", member.index), Span::call_site()) | |
| } | |
| }; | |
| quote!(#id) | |
| }) | |
| .collect(); | |
| wrap_serialize_with( | |
| params, | |
| serialize_with, | |
| field_tys.as_slice(), | |
| field_exprs.as_slice(), | |
| ) | |
| } | |
| fn wrap_serialize_with( | |
| params: &Parameters, | |
| serialize_with: &syn::ExprPath, | |
| field_tys: &[&syn::Type], | |
| field_exprs: &[TokenStream], | |
| ) -> TokenStream { | |
| let this = ¶ms.this; | |
| let (_, ty_generics, where_clause) = params.generics.split_for_impl(); | |
| let wrapper_generics = if field_exprs.is_empty() { | |
| params.generics.clone() | |
| } else { | |
| bound::with_lifetime_bound(¶ms.generics, "'__a") | |
| }; | |
| let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl(); | |
| let field_access = (0..field_exprs.len()).map(|n| { | |
| Member::Unnamed(Index { | |
| index: n as u32, | |
| span: Span::call_site(), | |
| }) | |
| }); | |
| quote!({ | |
| struct __SerializeWith #wrapper_impl_generics #where_clause { | |
| values: (#(&'__a #field_tys, )*), | |
| phantom: _serde::export::PhantomData<#this #ty_generics>, | |
| } | |
| impl #wrapper_impl_generics _serde::Serialize for __SerializeWith #wrapper_ty_generics #where_clause { | |
| fn serialize<__S>(&self, __s: __S) -> _serde::export::Result<__S::Ok, __S::Error> | |
| where | |
| __S: _serde::Serializer, | |
| { | |
| #serialize_with(#(self.values.#field_access, )* __s) | |
| } | |
| } | |
| &__SerializeWith { | |
| values: (#(#field_exprs, )*), | |
| phantom: _serde::export::PhantomData::<#this #ty_generics>, | |
| } | |
| }) | |
| } | |
| // Serialization of an empty struct results in code like: | |
| // | |
| // let mut __serde_state = try!(serializer.serialize_struct("S", 0)); | |
| // _serde::ser::SerializeStruct::end(__serde_state) | |
| // | |
| // where we want to omit the `mut` to avoid a warning. | |
| fn mut_if(is_mut: bool) -> Option<TokenStream> { | |
| if is_mut { | |
| Some(quote!(mut)) | |
| } else { | |
| None | |
| } | |
| } | |
| fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream { | |
| let self_var = ¶ms.self_var; | |
| match (params.is_remote, field.attrs.getter()) { | |
| (false, None) => quote!(&#self_var.#member), | |
| (true, None) => { | |
| let inner = quote!(&#self_var.#member); | |
| let ty = field.ty; | |
| quote!(_serde::private::ser::constrain::<#ty>(#inner)) | |
| } | |
| (true, Some(getter)) => { | |
| let ty = field.ty; | |
| quote!(_serde::private::ser::constrain::<#ty>(&#getter(#self_var))) | |
| } | |
| (false, Some(_)) => { | |
| unreachable!("getter is only allowed for remote impls"); | |
| } | |
| } | |
| } | |
| enum StructTrait { | |
| SerializeMap, | |
| SerializeStruct, | |
| SerializeStructVariant, | |
| } | |
| impl StructTrait { | |
| fn serialize_field(&self, span: Span) -> TokenStream { | |
| match *self { | |
| StructTrait::SerializeMap => { | |
| quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry) | |
| } | |
| StructTrait::SerializeStruct => { | |
| quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field) | |
| } | |
| StructTrait::SerializeStructVariant => { | |
| quote_spanned!(span=> _serde::ser::SerializeStructVariant::serialize_field) | |
| } | |
| } | |
| } | |
| fn skip_field(&self, span: Span) -> Option<TokenStream> { | |
| match *self { | |
| StructTrait::SerializeMap => None, | |
| StructTrait::SerializeStruct => { | |
| Some(quote_spanned!(span=> _serde::ser::SerializeStruct::skip_field)) | |
| } | |
| StructTrait::SerializeStructVariant => { | |
| Some(quote_spanned!(span=> _serde::ser::SerializeStructVariant::skip_field)) | |
| } | |
| } | |
| } | |
| } | |
| enum TupleTrait { | |
| SerializeTuple, | |
| SerializeTupleStruct, | |
| SerializeTupleVariant, | |
| } | |
| impl TupleTrait { | |
| fn serialize_element(&self, span: Span) -> TokenStream { | |
| match *self { | |
| TupleTrait::SerializeTuple => { | |
| quote_spanned!(span=> _serde::ser::SerializeTuple::serialize_element) | |
| } | |
| TupleTrait::SerializeTupleStruct => { | |
| quote_spanned!(span=> _serde::ser::SerializeTupleStruct::serialize_field) | |
| } | |
| TupleTrait::SerializeTupleVariant => { | |
| quote_spanned!(span=> _serde::ser::SerializeTupleVariant::serialize_field) | |
| } | |
| } | |
| } | |
| } |