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:
parent
4831482695
commit
5b815b7001
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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(¶ms.generics, "'__a");
|
||||
let wrapper_generics = if let Style::Unit = variant.style {
|
||||
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! {
|
||||
@ -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 = ¶ms.this;
|
||||
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
|
||||
let wrapper_generics = bound::with_lifetime_bound(¶ms.generics, "'__a");
|
||||
let wrapper_generics = if field_exprs.len() == 0 {
|
||||
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| 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>,
|
||||
}
|
||||
})
|
||||
|
@ -510,6 +510,8 @@ pub struct Variant {
|
||||
skip_deserializing: bool,
|
||||
skip_serializing: bool,
|
||||
other: bool,
|
||||
serialize_with: Option<syn::Path>,
|
||||
deserialize_with: Option<syn::Path>,
|
||||
}
|
||||
|
||||
impl Variant {
|
||||
@ -520,6 +522,8 @@ impl Variant {
|
||||
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
||||
let mut rename_all = Attr::none(cx, "rename_all");
|
||||
let mut other = BoolAttr::none(cx, "other");
|
||||
let mut serialize_with = Attr::none(cx, "serialize_with");
|
||||
let mut deserialize_with = Attr::none(cx, "deserialize_with");
|
||||
|
||||
for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) {
|
||||
for meta_item in meta_items {
|
||||
@ -569,6 +573,20 @@ impl Variant {
|
||||
other.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(serialize_with = "...")]`
|
||||
MetaItem(NameValue(ref name, ref lit)) if name == "serialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
|
||||
serialize_with.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(deserialize_with = "...")]`
|
||||
MetaItem(NameValue(ref name, ref lit)) if name == "deserialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
|
||||
deserialize_with.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
MetaItem(ref meta_item) => {
|
||||
cx.error(format!("unknown serde variant attribute `{}`", meta_item.name()));
|
||||
}
|
||||
@ -595,6 +613,8 @@ impl Variant {
|
||||
skip_deserializing: skip_deserializing.get(),
|
||||
skip_serializing: skip_serializing.get(),
|
||||
other: other.get(),
|
||||
serialize_with: serialize_with.get(),
|
||||
deserialize_with: deserialize_with.get(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -626,6 +646,14 @@ impl Variant {
|
||||
pub fn other(&self) -> bool {
|
||||
self.other
|
||||
}
|
||||
|
||||
pub fn serialize_with(&self) -> Option<&syn::Path> {
|
||||
self.serialize_with.as_ref()
|
||||
}
|
||||
|
||||
pub fn deserialize_with(&self) -> Option<&syn::Path> {
|
||||
self.deserialize_with.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents field attribute information
|
||||
|
@ -15,6 +15,7 @@ use Ctxt;
|
||||
pub fn check(cx: &Ctxt, cont: &Container) {
|
||||
check_getter(cx, cont);
|
||||
check_identifier(cx, cont);
|
||||
check_variant_skip_attrs(cx, cont);
|
||||
}
|
||||
|
||||
/// Getters are only allowed inside structs (not enums) with the `remote`
|
||||
@ -94,3 +95,40 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip-(de)serializing attributes are not allowed on variants marked
|
||||
/// (de)serialize_with.
|
||||
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
|
||||
let variants = match cont.body {
|
||||
Body::Enum(ref variants) => variants,
|
||||
Body::Struct(_, _) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for variant in variants.iter() {
|
||||
if variant.attrs.serialize_with().is_some() {
|
||||
if variant.attrs.skip_serializing() {
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
#[serde(skip_serializing)]", variant.ident));
|
||||
}
|
||||
|
||||
for (i, field) in variant.fields.iter().enumerate() {
|
||||
let ident = field.ident.as_ref().map_or_else(|| format!("{}", i),
|
||||
|ident| format!("`{}`", ident));
|
||||
|
||||
if field.attrs.skip_serializing() {
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
a field {} marked with #[serde(skip_serializing)]",
|
||||
variant.ident, ident));
|
||||
}
|
||||
|
||||
if field.attrs.skip_serializing_if().is_some() {
|
||||
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
a field {} marked with #[serde(skip_serializing_if)]",
|
||||
variant.ident, ident));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||
//~^ HELP: variant `Newtype` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing)]
|
||||
enum Enum {
|
||||
#[serde(serialize_with = "serialize_some_newtype_variant")]
|
||||
Newtype(#[serde(skip_serializing)] String),
|
||||
}
|
||||
|
||||
fn serialize_some_newtype_variant<S>(_: &String) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,27 @@
|
||||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||
//~^ HELP: variant `Newtype` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing_if)]
|
||||
enum Enum {
|
||||
#[serde(serialize_with = "serialize_some_newtype_variant")]
|
||||
Newtype(#[serde(skip_serializing_if = "always")] String),
|
||||
}
|
||||
|
||||
fn serialize_some_newtype_variant<S>(_: &String) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn always<T>(_: &T) -> bool { true }
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||
//~^ HELP: variant `Struct` cannot have both #[serde(serialize_with)] and a field `f1` marked with #[serde(skip_serializing)]
|
||||
enum Enum {
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
Struct {
|
||||
#[serde(skip_serializing)]
|
||||
f1: String,
|
||||
f2: u8,
|
||||
},
|
||||
}
|
||||
|
||||
fn serialize_some_other_variant<S>(_: &String, _: Option<&u8>, _: S) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||
//~^ HELP: variant `Struct` cannot have both #[serde(serialize_with)] and a field `f1` marked with #[serde(skip_serializing_if)]
|
||||
enum Enum {
|
||||
#[serde(serialize_with = "serialize_some_newtype_variant")]
|
||||
Struct {
|
||||
#[serde(skip_serializing_if = "always")]
|
||||
f1: String,
|
||||
f2: u8,
|
||||
},
|
||||
}
|
||||
|
||||
fn serialize_some_other_variant<S>(_: &String, _: Option<&u8>, _: S) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn always<T>(_: &T) -> bool { true }
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||
//~^ HELP: variant `Tuple` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing)]
|
||||
enum Enum {
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
Tuple(#[serde(skip_serializing)] String, u8),
|
||||
}
|
||||
|
||||
fn serialize_some_other_variant<S>(_: &String, _: Option<&u8>, _: S) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,27 @@
|
||||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||
//~^ HELP: variant `Tuple` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing_if)]
|
||||
enum Enum {
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
Tuple(#[serde(skip_serializing_if = "always")] String, u8),
|
||||
}
|
||||
|
||||
fn serialize_some_other_variant<S>(_: &String, _: Option<&u8>, _: S) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn always<T>(_: &T) -> bool { true }
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||
//~^ HELP: variant `Unit` cannot have both #[serde(serialize_with)] and #[serde(skip_serializing)]
|
||||
enum Enum {
|
||||
#[serde(serialize_with = "serialize_some_unit_variant")]
|
||||
#[serde(skip_serializing)]
|
||||
Unit,
|
||||
}
|
||||
|
||||
fn serialize_some_unit_variant<S>(_: S) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -811,6 +811,74 @@ fn test_serialize_with_enum() {
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
enum SerializeWithVariant {
|
||||
#[serde(serialize_with="serialize_unit_variant_as_i8")]
|
||||
Unit,
|
||||
|
||||
#[serde(serialize_with="SerializeWith::serialize_with")]
|
||||
Newtype(i32),
|
||||
|
||||
#[serde(serialize_with="serialize_some_other_variant")]
|
||||
Tuple(String, u8),
|
||||
|
||||
#[serde(serialize_with="serialize_some_other_variant")]
|
||||
Struct {
|
||||
f1: String,
|
||||
f2: u8,
|
||||
},
|
||||
}
|
||||
|
||||
fn serialize_unit_variant_as_i8<S>(serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
serializer.serialize_i8(0)
|
||||
}
|
||||
|
||||
fn serialize_some_other_variant<S>(f1: &String,
|
||||
f2: &u8,
|
||||
serializer: S)
|
||||
-> Result<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(format!("{};{:?}", f1, f2).as_str())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_with_variant() {
|
||||
assert_ser_tokens(
|
||||
&SerializeWithVariant::Unit,
|
||||
&[
|
||||
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Unit" },
|
||||
Token::I8(0),
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SerializeWithVariant::Newtype(123),
|
||||
&[
|
||||
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Newtype" },
|
||||
Token::Bool(true),
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SerializeWithVariant::Tuple("hello".into(), 0),
|
||||
&[
|
||||
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Tuple" },
|
||||
Token::Str("hello;0"),
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SerializeWithVariant::Struct { f1: "world".into(), f2: 1 },
|
||||
&[
|
||||
Token::NewtypeVariant { name: "SerializeWithVariant", variant: "Struct" },
|
||||
Token::Str("world;1"),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct DeserializeWithStruct<B>
|
||||
where
|
||||
|
@ -373,6 +373,124 @@ fn test_gen() {
|
||||
#[serde(with = "vis::SDef")]
|
||||
s: vis::S,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum ExternallyTaggedVariantWith {
|
||||
#[allow(dead_code)]
|
||||
Normal { f1: String },
|
||||
|
||||
#[serde(serialize_with = "ser_x")]
|
||||
#[serde(deserialize_with = "de_x")]
|
||||
#[allow(dead_code)]
|
||||
Newtype(X),
|
||||
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_other_variant")]
|
||||
#[allow(dead_code)]
|
||||
Tuple(String, u8),
|
||||
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_other_variant")]
|
||||
#[allow(dead_code)]
|
||||
Struct {
|
||||
f1: String,
|
||||
f2: u8,
|
||||
},
|
||||
|
||||
#[serde(serialize_with = "serialize_some_unit_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_unit_variant")]
|
||||
#[allow(dead_code)]
|
||||
Unit,
|
||||
}
|
||||
assert_ser::<ExternallyTaggedVariantWith>();
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "t")]
|
||||
enum InternallyTaggedVariantWith {
|
||||
#[allow(dead_code)]
|
||||
Normal { f1: String },
|
||||
|
||||
#[serde(serialize_with = "ser_x")]
|
||||
#[serde(deserialize_with = "de_x")]
|
||||
#[allow(dead_code)]
|
||||
Newtype(X),
|
||||
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_other_variant")]
|
||||
#[allow(dead_code)]
|
||||
Struct {
|
||||
f1: String,
|
||||
f2: u8,
|
||||
},
|
||||
|
||||
#[serde(serialize_with = "serialize_some_unit_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_unit_variant")]
|
||||
#[allow(dead_code)]
|
||||
Unit,
|
||||
}
|
||||
assert_ser::<InternallyTaggedVariantWith>();
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
enum AdjacentlyTaggedVariantWith {
|
||||
#[allow(dead_code)]
|
||||
Normal { f1: String },
|
||||
|
||||
#[serde(serialize_with = "ser_x")]
|
||||
#[serde(deserialize_with = "de_x")]
|
||||
#[allow(dead_code)]
|
||||
Newtype(X),
|
||||
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_other_variant")]
|
||||
#[allow(dead_code)]
|
||||
Tuple(String, u8),
|
||||
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_other_variant")]
|
||||
#[allow(dead_code)]
|
||||
Struct {
|
||||
f1: String,
|
||||
f2: u8,
|
||||
},
|
||||
|
||||
#[serde(serialize_with = "serialize_some_unit_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_unit_variant")]
|
||||
#[allow(dead_code)]
|
||||
Unit,
|
||||
}
|
||||
assert_ser::<AdjacentlyTaggedVariantWith>();
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum UntaggedVariantWith {
|
||||
#[allow(dead_code)]
|
||||
Normal { f1: String },
|
||||
|
||||
#[serde(serialize_with = "ser_x")]
|
||||
#[serde(deserialize_with = "de_x")]
|
||||
#[allow(dead_code)]
|
||||
Newtype(X),
|
||||
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_other_variant")]
|
||||
#[allow(dead_code)]
|
||||
Tuple(String, u8),
|
||||
|
||||
#[serde(serialize_with = "serialize_some_other_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_other_variant")]
|
||||
#[allow(dead_code)]
|
||||
Struct {
|
||||
f1: String,
|
||||
f2: u8,
|
||||
},
|
||||
|
||||
#[serde(serialize_with = "serialize_some_unit_variant")]
|
||||
#[serde(deserialize_with = "deserialize_some_unit_variant")]
|
||||
#[allow(dead_code)]
|
||||
Unit,
|
||||
}
|
||||
assert_ser::<UntaggedVariantWith>();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -414,3 +532,29 @@ impl DeserializeWith for X {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_some_unit_variant<S>(_: S) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn deserialize_some_unit_variant<'de, D>(_: D) -> StdResult<(), D::Error>
|
||||
where D: Deserializer<'de>,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn serialize_some_other_variant<S>(_: &String, _: &u8, _: S) -> StdResult<S::Ok, S::Error>
|
||||
where S: Serializer,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn deserialize_some_other_variant<'de, D>(_: D) -> StdResult<(String, u8), D::Error>
|
||||
where D: Deserializer<'de>,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn is_zero(n: &u8) -> bool { *n == 0 }
|
||||
|
Loading…
Reference in New Issue
Block a user