Permalink
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up| 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, Vec<syn::Error>> { | |
| let ctxt = Ctxt::new(); | |
| let cont = match Container::from_ast(&ctxt, input, Derive::Serialize) { | |
| Some(cont) => cont, | |
| None => return Err(ctxt.check().unwrap_err()), | |
| }; | |
| 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 suffix = ident.to_string().trim_left_matches("r#").to_owned(); | |
| let dummy_const = Ident::new( | |
| &format!("_IMPL_SERIALIZE_FOR_{}", suffix), | |
| 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: () = { | |
| #[allow(unknown_lints)] | |
| #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))] | |
| #[allow(rust_2018_idioms)] | |
| 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_spanned_by(cont.original, "field identifiers cannot be serialized"); | |
| } | |
| attr::Identifier::Variant => { | |
| cx.error_spanned_by(cont.original, "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 => { | |
| let span = transparent_field.original.span(); | |
| quote_spanned!(span=> _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); | |
| } | |
| let span = field.original.span(); | |
| let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_variant); | |
| quote_expr! { | |
| #func( | |
| __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); | |
| } | |
| let span = field.original.span(); | |
| let func = quote_spanned!(span=> _serde::private::ser::serialize_tagged_newtype); | |
| quote_expr! { | |
| #func( | |
| __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); | |
| } | |
| let span = field.original.span(); | |
| let func = quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field); | |
| 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!(#func( | |
| &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); | |
| } | |
| let span = field.original.span(); | |
| let func = quote_spanned!(span=> _serde::Serialize::serialize); | |
| quote_expr! { | |
| #func(#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() { | |
| let func = quote_spanned!(span=> _serde::Serialize::serialize); | |
| quote! { | |
| try!(#func(&#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) | |
| } | |
| } | |
| } | |
| } |