Implement serialize_with for variants

As discussed in #1013, serialize_with functions attached to variants receive an
argument for each inner value contained within the variant. Internally such a
function is wired up to the serializer as if the variant were a newtype variant.
This commit is contained in:
Michael Smith
2017-08-07 17:22:26 -07:00
parent 4831482695
commit 5b815b7001
14 changed files with 631 additions and 60 deletions
+19 -9
View File
@@ -10,7 +10,7 @@ use std::collections::HashSet;
use syn::{self, visit};
use internals::ast::Container;
use internals::ast::{Body, Container};
use internals::attr;
macro_rules! path {
@@ -88,7 +88,7 @@ pub fn with_bound<F>(
bound: &syn::Path,
) -> syn::Generics
where
F: Fn(&attr::Field) -> bool,
F: Fn(&attr::Field, Option<&attr::Variant>) -> bool,
{
struct FindTyParams {
// Set of all generic type parameters on the current struct (A, B, C in
@@ -124,17 +124,27 @@ where
.map(|ty_param| ty_param.ident.clone())
.collect();
let relevant_tys = cont.body
.all_fields()
.filter(|&field| filter(&field.attrs))
.map(|field| &field.ty);
let mut visitor = FindTyParams {
all_ty_params: all_ty_params,
relevant_ty_params: HashSet::new(),
};
for ty in relevant_tys {
visit::walk_ty(&mut visitor, ty);
match cont.body {
Body::Enum(ref variants) => {
for variant in variants.iter() {
let relevant_fields = variant
.fields
.iter()
.filter(|field| filter(&field.attrs, Some(&variant.attrs)));
for field in relevant_fields {
visit::walk_ty(&mut visitor, field.ty);
}
}
}
Body::Struct(_, ref fields) => {
for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
visit::walk_ty(&mut visitor, field.ty);
}
}
}
let new_predicates = generics
+7 -4
View File
@@ -157,14 +157,17 @@ fn build_generics(cont: &Container) -> syn::Generics {
// deserialized 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: Deserialize` bound where T is the type of the field.
fn needs_deserialize_bound(attrs: &attr::Field) -> bool {
!attrs.skip_deserializing() && attrs.deserialize_with().is_none() && attrs.de_bound().is_none()
fn needs_deserialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
!field.skip_deserializing() &&
field.deserialize_with().is_none() &&
field.de_bound().is_none() &&
variant.map_or(true, |variant| variant.deserialize_with().is_none())
}
// Fields with a `default` attribute (not `default=...`), and fields with a
// `skip_deserializing` attribute that do not also have `default=...`.
fn requires_default(attrs: &attr::Field) -> bool {
attrs.default() == &attr::Default::Default
fn requires_default(field: &attr::Field, _variant: Option<&attr::Variant>) -> bool {
field.default() == &attr::Default::Default
}
// The union of lifetimes borrowed by each field of the container.
+137 -47
View File
@@ -143,12 +143,16 @@ fn build_generics(cont: &Container) -> syn::Generics {
}
}
// Fields 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(attrs: &attr::Field) -> bool {
!attrs.skip_serializing() && attrs.serialize_with().is_none() && attrs.ser_bound().is_none()
// Fields with a `skip_serializing` or `serialize_with` attribute, or which
// belong to a variant with a `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.serialize_with().is_none())
}
fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
@@ -203,7 +207,7 @@ fn serialize_newtype_struct(
let mut field_expr = get_field(params, field, 0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -395,6 +399,19 @@ fn serialize_externally_tagged_variant(
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! {
@@ -410,7 +427,7 @@ fn serialize_externally_tagged_variant(
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -460,6 +477,20 @@ fn serialize_internally_tagged_variant(
let enum_ident_str = params.type_name();
let variant_ident_str = variant.ident.as_ref();
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! {
@@ -474,7 +505,7 @@ fn serialize_internally_tagged_variant(
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -515,44 +546,57 @@ fn serialize_adjacently_tagged_variant(
let variant_name = variant.attrs.name().serialize_name();
let inner = Stmts(
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)
};
if let Some(path) = variant.attrs.serialize_with() {
let ser = wrap_serialize_variant_with(params, path, &variant);
quote_expr! {
_serde::Serialize::serialize(#ser, __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_with(params, field.ty, path, field_expr);
} 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);
}
quote_expr! {
_serde::Serialize::serialize(#field_expr, __serializer)
quote_expr! {
_serde::Serialize::serialize(#field_expr, __serializer)
}
}
Style::Tuple => {
serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
}
Style::Struct => {
serialize_struct_variant(
StructVariant::Untagged,
params,
&variant.fields,
&variant_name,
)
}
}
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 ref fields_ident: Vec<_> = match variant.style {
Style::Unit => unreachable!(),
Style::Unit => {
if variant.attrs.serialize_with().is_some() {
vec![]
} else {
unreachable!()
}
}
Style::Newtype => vec![Ident::new("__field0")],
Style::Tuple => {
(0..variant.fields.len())
@@ -576,7 +620,11 @@ fn serialize_adjacently_tagged_variant(
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
let wrapper_generics = bound::with_lifetime_bound(&params.generics, "'__a");
let wrapper_generics = if let Style::Unit = variant.style {
params.generics.clone()
} else {
bound::with_lifetime_bound(&params.generics, "'__a")
};
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
quote_block! {
@@ -612,6 +660,13 @@ fn serialize_untagged_variant(
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! {
@@ -622,7 +677,7 @@ fn serialize_untagged_variant(
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -808,7 +863,7 @@ fn serialize_tuple_struct_visitor(
.map(|path| quote!(#path(#field_expr)));
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
let ser = quote! {
@@ -850,7 +905,7 @@ fn serialize_struct_visitor(
.map(|path| quote!(#path(#field_expr)));
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr)
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
let ser = quote! {
@@ -866,21 +921,56 @@ fn serialize_struct_visitor(
.collect()
}
fn wrap_serialize_with(
fn wrap_serialize_field_with(
params: &Parameters,
field_ty: &syn::Ty,
serialize_with: &syn::Path,
value: Tokens,
field_expr: Tokens,
) -> Tokens {
wrap_serialize_with(params,
serialize_with,
&[field_ty],
&[quote!(#field_expr)])
}
fn wrap_serialize_variant_with(
params: &Parameters,
serialize_with: &syn::Path,
variant: &Variant,
) -> Tokens {
let field_tys: Vec<_> = variant.fields.iter().map(|field| field.ty).collect();
let field_exprs: Vec<_> = variant.fields.iter()
.enumerate()
.map(|(i, field)| {
let id = field.ident.as_ref().map_or_else(|| Ident::new(format!("__field{}", i)),
|id| id.clone());
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::Path,
field_tys: &[&syn::Ty],
field_exprs: &[Tokens],
) -> Tokens {
let this = &params.this;
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
let wrapper_generics = bound::with_lifetime_bound(&params.generics, "'__a");
let wrapper_generics = if field_exprs.len() == 0 {
params.generics.clone()
} else {
bound::with_lifetime_bound(&params.generics, "'__a")
};
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
let field_access = (0..field_exprs.len()).map(|n| Ident::new(format!("{}", n)));
quote!({
struct __SerializeWith #wrapper_impl_generics #where_clause {
value: &'__a #field_ty,
values: (#(&'__a #field_tys, )*),
phantom: _serde::export::PhantomData<#this #ty_generics>,
}
@@ -888,12 +978,12 @@ fn wrap_serialize_with(
fn serialize<__S>(&self, __s: __S) -> _serde::export::Result<__S::Ok, __S::Error>
where __S: _serde::Serializer
{
#serialize_with(self.value, __s)
#serialize_with(#(self.values.#field_access, )* __s)
}
}
&__SerializeWith {
value: #value,
values: (#(#field_exprs, )*),
phantom: _serde::export::PhantomData::<#this #ty_generics>,
}
})