mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-27 21:37:55 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6b62b9417 | |||
| 2ee347c5a5 | |||
| 4305260174 | |||
| 35aae92b56 | |||
| f3f26796c7 | |||
| d1460e1f0d | |||
| dfd81323d5 | |||
| 368961e961 | |||
| f9c6f0ab62 | |||
| b2b36e1764 | |||
| 4ad140ea70 | |||
| 67777eb585 | |||
| b4e51fcc77 | |||
| be7fe2a5eb | |||
| b4076f4577 | |||
| c4181f46be | |||
| 8c0efc3d77 | |||
| 7e3efaf6c5 | |||
| 12fe42ed45 | |||
| 7cd4f49c76 | |||
| ff9c85d47f | |||
| 0025ef9aba |
+1
-6
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.49" # remember to update html_root_url
|
version = "1.0.54" # remember to update html_root_url
|
||||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
description = "A generic serialization/deserialization framework"
|
description = "A generic serialization/deserialization framework"
|
||||||
@@ -61,8 +61,3 @@ alloc = ["unstable"]
|
|||||||
# does not preserve identity and may result in multiple copies of the same data.
|
# does not preserve identity and may result in multiple copies of the same data.
|
||||||
# Be sure that this is what you want before enabling this feature.
|
# Be sure that this is what you want before enabling this feature.
|
||||||
rc = []
|
rc = []
|
||||||
|
|
||||||
# Get serde_derive picked up by the Integer 32 playground. Not public API.
|
|
||||||
#
|
|
||||||
# http://play.integer32.com/
|
|
||||||
playground = ["serde_derive"]
|
|
||||||
|
|||||||
@@ -529,6 +529,14 @@ where
|
|||||||
{
|
{
|
||||||
T::deserialize(deserializer).map(Some)
|
T::deserialize(deserializer).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn __private_visit_untagged_option<D>(self, deserializer: D) -> Result<Self::Value, ()>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(T::deserialize(deserializer).ok())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, T> Deserialize<'de> for Option<T>
|
impl<'de, T> Deserialize<'de> for Option<T>
|
||||||
|
|||||||
+9
-12
@@ -1132,18 +1132,6 @@ pub trait Deserializer<'de>: Sized {
|
|||||||
fn is_human_readable(&self) -> bool {
|
fn is_human_readable(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not public API.
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn private_deserialize_internally_tagged_enum<V>(
|
|
||||||
self,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value, Self::Error>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
self.deserialize_any(visitor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1541,6 +1529,15 @@ pub trait Visitor<'de>: Sized {
|
|||||||
let _ = data;
|
let _ = data;
|
||||||
Err(Error::invalid_type(Unexpected::Enum, &self))
|
Err(Error::invalid_type(Unexpected::Enum, &self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used when deserializing a flattened Option field. Not public API.
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn __private_visit_untagged_option<D>(self, _: D) -> Result<Self::Value, ()>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
+1
-1
@@ -79,7 +79,7 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Serde types in rustdoc of other crates get linked to here.
|
// Serde types in rustdoc of other crates get linked to here.
|
||||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.49")]
|
#![doc(html_root_url = "https://docs.rs/serde/1.0.54")]
|
||||||
// Support using Serde without the standard library!
|
// Support using Serde without the standard library!
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
// Unstable functionality only if the user asks for it. For tracking and
|
// Unstable functionality only if the user asks for it. For tracking and
|
||||||
|
|||||||
+61
-17
@@ -2640,6 +2640,30 @@ pub struct FlatMapDeserializer<'a, 'de: 'a, E>(
|
|||||||
pub PhantomData<E>,
|
pub PhantomData<E>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
|
impl<'a, 'de, E> FlatMapDeserializer<'a, 'de, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
fn deserialize_other<V>() -> Result<V, E> {
|
||||||
|
Err(Error::custom("can only flatten structs and maps"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
|
macro_rules! forward_to_deserialize_other {
|
||||||
|
($($func:ident ( $($arg:ty),* ))*) => {
|
||||||
|
$(
|
||||||
|
fn $func<V>(self, $(_: $arg,)* _visitor: V) -> Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
Self::deserialize_other()
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
impl<'a, 'de, E> Deserializer<'de> for FlatMapDeserializer<'a, 'de, E>
|
impl<'a, 'de, E> Deserializer<'de> for FlatMapDeserializer<'a, 'de, E>
|
||||||
where
|
where
|
||||||
@@ -2647,11 +2671,15 @@ where
|
|||||||
{
|
{
|
||||||
type Error = E;
|
type Error = E;
|
||||||
|
|
||||||
fn deserialize_any<V>(self, _: V) -> Result<V::Value, Self::Error>
|
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: Visitor<'de>,
|
V: Visitor<'de>,
|
||||||
{
|
{
|
||||||
Err(Error::custom("can only flatten structs and maps"))
|
visitor.visit_map(FlatInternallyTaggedAccess {
|
||||||
|
iter: self.0.iter_mut(),
|
||||||
|
pending: None,
|
||||||
|
_marker: PhantomData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_enum<V>(
|
fn deserialize_enum<V>(
|
||||||
@@ -2710,24 +2738,40 @@ where
|
|||||||
visitor.visit_newtype_struct(self)
|
visitor.visit_newtype_struct(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
forward_to_deserialize_any! {
|
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
|
|
||||||
byte_buf option unit unit_struct seq tuple tuple_struct identifier
|
|
||||||
ignored_any
|
|
||||||
}
|
|
||||||
|
|
||||||
fn private_deserialize_internally_tagged_enum<V>(
|
|
||||||
self,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value, Self::Error>
|
|
||||||
where
|
where
|
||||||
V: Visitor<'de>,
|
V: Visitor<'de>,
|
||||||
{
|
{
|
||||||
visitor.visit_map(FlatInternallyTaggedAccess {
|
match visitor.__private_visit_untagged_option(self) {
|
||||||
iter: self.0.iter_mut(),
|
Ok(value) => Ok(value),
|
||||||
pending: None,
|
Err(()) => Self::deserialize_other(),
|
||||||
_marker: PhantomData,
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
forward_to_deserialize_other! {
|
||||||
|
deserialize_bool()
|
||||||
|
deserialize_i8()
|
||||||
|
deserialize_i16()
|
||||||
|
deserialize_i32()
|
||||||
|
deserialize_i64()
|
||||||
|
deserialize_u8()
|
||||||
|
deserialize_u16()
|
||||||
|
deserialize_u32()
|
||||||
|
deserialize_u64()
|
||||||
|
deserialize_f32()
|
||||||
|
deserialize_f64()
|
||||||
|
deserialize_char()
|
||||||
|
deserialize_str()
|
||||||
|
deserialize_string()
|
||||||
|
deserialize_bytes()
|
||||||
|
deserialize_byte_buf()
|
||||||
|
deserialize_unit()
|
||||||
|
deserialize_unit_struct(&'static str)
|
||||||
|
deserialize_seq()
|
||||||
|
deserialize_tuple(usize)
|
||||||
|
deserialize_tuple_struct(&'static str, usize)
|
||||||
|
deserialize_identifier()
|
||||||
|
deserialize_ignored_any()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1122,14 +1122,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||||
Err(self.bad_type(Unsupported::Optional))
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_some<T: ?Sized>(self, _: &T) -> Result<Self::Ok, Self::Error>
|
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
Err(self.bad_type(Unsupported::Optional))
|
value.serialize(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.49" # remember to update html_root_url
|
version = "1.0.54" # remember to update html_root_url
|
||||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
|
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
|
||||||
|
|||||||
@@ -65,6 +65,28 @@ pub fn with_where_predicates_from_fields(
|
|||||||
generics
|
generics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_where_predicates_from_variants(
|
||||||
|
cont: &Container,
|
||||||
|
generics: &syn::Generics,
|
||||||
|
from_variant: fn(&attr::Variant) -> Option<&[syn::WherePredicate]>,
|
||||||
|
) -> syn::Generics {
|
||||||
|
let variants = match cont.data {
|
||||||
|
Data::Enum(ref variants) => variants,
|
||||||
|
Data::Struct(_, _) => {
|
||||||
|
return generics.clone();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let predicates = variants
|
||||||
|
.iter()
|
||||||
|
.flat_map(|variant| from_variant(&variant.attrs))
|
||||||
|
.flat_map(|predicates| predicates.to_vec());
|
||||||
|
|
||||||
|
let mut generics = generics.clone();
|
||||||
|
generics.make_where_clause().predicates.extend(predicates);
|
||||||
|
generics
|
||||||
|
}
|
||||||
|
|
||||||
// Puts the given bound on any generic type parameters that are used in fields
|
// Puts the given bound on any generic type parameters that are used in fields
|
||||||
// for which filter returns true.
|
// for which filter returns true.
|
||||||
//
|
//
|
||||||
|
|||||||
+79
-21
@@ -15,15 +15,16 @@ use syn::{self, Ident, Index, Member};
|
|||||||
use bound;
|
use bound;
|
||||||
use fragment::{Expr, Fragment, Match, Stmts};
|
use fragment::{Expr, Fragment, Match, Stmts};
|
||||||
use internals::ast::{Container, Data, Field, Style, Variant};
|
use internals::ast::{Container, Data, Field, Style, Variant};
|
||||||
use internals::{self, attr};
|
use internals::{attr, Ctxt};
|
||||||
use pretend;
|
use pretend;
|
||||||
use try;
|
use try;
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<Tokens, String> {
|
pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<Tokens, String> {
|
||||||
let ctxt = internals::Ctxt::new();
|
let ctxt = Ctxt::new();
|
||||||
let cont = Container::from_ast(&ctxt, input);
|
let cont = Container::from_ast(&ctxt, input);
|
||||||
|
precondition(&ctxt, &cont);
|
||||||
try!(ctxt.check());
|
try!(ctxt.check());
|
||||||
|
|
||||||
let ident = cont.ident;
|
let ident = cont.ident;
|
||||||
@@ -80,6 +81,32 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<Tokens, Str
|
|||||||
Ok(generated)
|
Ok(generated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn precondition(cx: &Ctxt, cont: &Container) {
|
||||||
|
precondition_sized(cx, cont);
|
||||||
|
precondition_no_de_lifetime(cx, cont);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precondition_sized(cx: &Ctxt, cont: &Container) {
|
||||||
|
if let Data::Struct(_, ref fields) = cont.data {
|
||||||
|
if let Some(last) = fields.last() {
|
||||||
|
if let syn::Type::Slice(_) = *last.ty {
|
||||||
|
cx.error("cannot deserialize a dynamically sized struct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precondition_no_de_lifetime(cx: &Ctxt, cont: &Container) {
|
||||||
|
if let BorrowedLifetimes::Borrowed(_) = borrowed_lifetimes(cont) {
|
||||||
|
for param in cont.generics.lifetimes() {
|
||||||
|
if param.lifetime.to_string() == "'de" {
|
||||||
|
cx.error("cannot deserialize when there is a lifetime parameter called 'de");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
/// Name of the type the `derive` is on.
|
/// Name of the type the `derive` is on.
|
||||||
local: syn::Ident,
|
local: syn::Ident,
|
||||||
@@ -136,6 +163,9 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi
|
|||||||
|
|
||||||
let generics = bound::with_where_predicates_from_fields(cont, &generics, attr::Field::de_bound);
|
let generics = bound::with_where_predicates_from_fields(cont, &generics, attr::Field::de_bound);
|
||||||
|
|
||||||
|
let generics =
|
||||||
|
bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::de_bound);
|
||||||
|
|
||||||
match cont.attrs.de_bound() {
|
match cont.attrs.de_bound() {
|
||||||
Some(predicates) => bound::with_where_predicates(&generics, predicates),
|
Some(predicates) => bound::with_where_predicates(&generics, predicates),
|
||||||
None => {
|
None => {
|
||||||
@@ -164,13 +194,18 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields with a `skip_deserializing` or `deserialize_with` attribute are not
|
// Fields with a `skip_deserializing` or `deserialize_with` attribute, or which
|
||||||
// deserialized by us so we do not generate a bound. Fields with a `bound`
|
// belong to a variant with a `skip_deserializing` or `deserialize_with`
|
||||||
// attribute specify their own bound so we do not generate one. All other fields
|
// attribute, are not deserialized by us so we do not generate a bound. Fields
|
||||||
// may need a `T: Deserialize` bound where T is the type of the field.
|
// 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(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
|
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()
|
!field.skip_deserializing() && field.deserialize_with().is_none() && field.de_bound().is_none()
|
||||||
&& variant.map_or(true, |variant| variant.deserialize_with().is_none())
|
&& variant.map_or(true, |variant| {
|
||||||
|
!variant.skip_deserializing() && variant.deserialize_with().is_none()
|
||||||
|
&& variant.de_bound().is_none()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields with a `default` attribute (not `default=...`), and fields with a
|
// Fields with a `default` attribute (not `default=...`), and fields with a
|
||||||
@@ -392,7 +427,9 @@ fn deserialize_tuple(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let visit_seq = Stmts(deserialize_seq(&type_path, params, fields, false, cattrs));
|
let visit_seq = Stmts(deserialize_seq(
|
||||||
|
&type_path, params, fields, false, cattrs, &expecting,
|
||||||
|
));
|
||||||
|
|
||||||
let visitor_expr = quote! {
|
let visitor_expr = quote! {
|
||||||
__Visitor {
|
__Visitor {
|
||||||
@@ -476,7 +513,7 @@ fn deserialize_tuple_in_place(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs));
|
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
|
||||||
|
|
||||||
let visitor_expr = quote! {
|
let visitor_expr = quote! {
|
||||||
__Visitor {
|
__Visitor {
|
||||||
@@ -542,6 +579,7 @@ fn deserialize_seq(
|
|||||||
fields: &[Field],
|
fields: &[Field],
|
||||||
is_struct: bool,
|
is_struct: bool,
|
||||||
cattrs: &attr::Container,
|
cattrs: &attr::Container,
|
||||||
|
expecting: &str,
|
||||||
) -> Fragment {
|
) -> Fragment {
|
||||||
let vars = (0..fields.len()).map(field_i as fn(_) -> _);
|
let vars = (0..fields.len()).map(field_i as fn(_) -> _);
|
||||||
|
|
||||||
@@ -551,7 +589,11 @@ fn deserialize_seq(
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|field| !field.attrs.skip_deserializing())
|
.filter(|field| !field.attrs.skip_deserializing())
|
||||||
.count();
|
.count();
|
||||||
let expecting = format!("tuple of {} elements", deserialized_count);
|
let expecting = if deserialized_count == 1 {
|
||||||
|
format!("{} with 1 element", expecting)
|
||||||
|
} else {
|
||||||
|
format!("{} with {} elements", expecting, deserialized_count)
|
||||||
|
};
|
||||||
|
|
||||||
let mut index_in_seq = 0_usize;
|
let mut index_in_seq = 0_usize;
|
||||||
let let_values = vars.clone().zip(fields).map(|(var, field)| {
|
let let_values = vars.clone().zip(fields).map(|(var, field)| {
|
||||||
@@ -636,6 +678,7 @@ fn deserialize_seq_in_place(
|
|||||||
params: &Parameters,
|
params: &Parameters,
|
||||||
fields: &[Field],
|
fields: &[Field],
|
||||||
cattrs: &attr::Container,
|
cattrs: &attr::Container,
|
||||||
|
expecting: &str,
|
||||||
) -> Fragment {
|
) -> Fragment {
|
||||||
let vars = (0..fields.len()).map(field_i as fn(_) -> _);
|
let vars = (0..fields.len()).map(field_i as fn(_) -> _);
|
||||||
|
|
||||||
@@ -645,7 +688,11 @@ fn deserialize_seq_in_place(
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|field| !field.attrs.skip_deserializing())
|
.filter(|field| !field.attrs.skip_deserializing())
|
||||||
.count();
|
.count();
|
||||||
let expecting = format!("tuple of {} elements", deserialized_count);
|
let expecting = if deserialized_count == 1 {
|
||||||
|
format!("{} with 1 element", expecting)
|
||||||
|
} else {
|
||||||
|
format!("{} with {} elements", expecting, deserialized_count)
|
||||||
|
};
|
||||||
|
|
||||||
let mut index_in_seq = 0usize;
|
let mut index_in_seq = 0usize;
|
||||||
let write_values = vars.clone()
|
let write_values = vars.clone()
|
||||||
@@ -817,7 +864,9 @@ fn deserialize_struct(
|
|||||||
None => format!("struct {}", params.type_name()),
|
None => format!("struct {}", params.type_name()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let visit_seq = Stmts(deserialize_seq(&type_path, params, fields, true, cattrs));
|
let visit_seq = Stmts(deserialize_seq(
|
||||||
|
&type_path, params, fields, true, cattrs, &expecting,
|
||||||
|
));
|
||||||
|
|
||||||
let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() {
|
let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() {
|
||||||
deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs)
|
deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs)
|
||||||
@@ -956,7 +1005,7 @@ fn deserialize_struct_in_place(
|
|||||||
None => format!("struct {}", params.type_name()),
|
None => format!("struct {}", params.type_name()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs));
|
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
|
||||||
|
|
||||||
let (field_visitor, fields_stmt, visit_map) =
|
let (field_visitor, fields_stmt, visit_map) =
|
||||||
deserialize_struct_as_struct_in_place_visitor(params, fields, cattrs);
|
deserialize_struct_as_struct_in_place_visitor(params, fields, cattrs);
|
||||||
@@ -1216,7 +1265,7 @@ fn deserialize_internally_tagged_enum(
|
|||||||
|
|
||||||
#variants_stmt
|
#variants_stmt
|
||||||
|
|
||||||
let __tagged = try!(_serde::Deserializer::private_deserialize_internally_tagged_enum(
|
let __tagged = try!(_serde::Deserializer::deserialize_any(
|
||||||
__deserializer,
|
__deserializer,
|
||||||
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
|
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
|
||||||
|
|
||||||
@@ -1382,6 +1431,21 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let finish_content_then_tag = if variant_arms.is_empty() {
|
||||||
|
quote! {
|
||||||
|
match try!(_serde::de::MapAccess::next_value::<__Field>(&mut __map)) {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
let __ret = try!(match try!(_serde::de::MapAccess::next_value(&mut __map)) {
|
||||||
|
// Deserialize the buffered content now that we know the variant.
|
||||||
|
#(#variant_arms)*
|
||||||
|
});
|
||||||
|
// Visit remaining keys, looking for duplicates.
|
||||||
|
#visit_remaining_keys
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
quote_block! {
|
quote_block! {
|
||||||
#variant_visitor
|
#variant_visitor
|
||||||
|
|
||||||
@@ -1458,13 +1522,7 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
// Second key is the tag.
|
// Second key is the tag.
|
||||||
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
||||||
let __deserializer = _serde::private::de::ContentDeserializer::<__A::Error>::new(__content);
|
let __deserializer = _serde::private::de::ContentDeserializer::<__A::Error>::new(__content);
|
||||||
// Parse the tag.
|
#finish_content_then_tag
|
||||||
let __ret = try!(match try!(_serde::de::MapAccess::next_value(&mut __map)) {
|
|
||||||
// Deserialize the buffered content now that we know the variant.
|
|
||||||
#(#variant_arms)*
|
|
||||||
});
|
|
||||||
// Visit remaining keys, looking for duplicates.
|
|
||||||
#visit_remaining_keys
|
|
||||||
}
|
}
|
||||||
// Second key is a duplicate of the content.
|
// Second key is a duplicate of the content.
|
||||||
_serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
|
_serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
|
||||||
|
|||||||
@@ -527,6 +527,8 @@ pub struct Variant {
|
|||||||
ser_renamed: bool,
|
ser_renamed: bool,
|
||||||
de_renamed: bool,
|
de_renamed: bool,
|
||||||
rename_all: RenameRule,
|
rename_all: RenameRule,
|
||||||
|
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
|
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
skip_deserializing: bool,
|
skip_deserializing: bool,
|
||||||
skip_serializing: bool,
|
skip_serializing: bool,
|
||||||
other: bool,
|
other: bool,
|
||||||
@@ -542,6 +544,8 @@ impl Variant {
|
|||||||
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
|
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
|
||||||
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
||||||
let mut rename_all = Attr::none(cx, "rename_all");
|
let mut rename_all = Attr::none(cx, "rename_all");
|
||||||
|
let mut ser_bound = Attr::none(cx, "bound");
|
||||||
|
let mut de_bound = Attr::none(cx, "bound");
|
||||||
let mut other = BoolAttr::none(cx, "other");
|
let mut other = BoolAttr::none(cx, "other");
|
||||||
let mut serialize_with = Attr::none(cx, "serialize_with");
|
let mut serialize_with = Attr::none(cx, "serialize_with");
|
||||||
let mut deserialize_with = Attr::none(cx, "deserialize_with");
|
let mut deserialize_with = Attr::none(cx, "deserialize_with");
|
||||||
@@ -580,6 +584,12 @@ impl Variant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse `#[serde(skip)]`
|
||||||
|
Meta(Word(word)) if word == "skip" => {
|
||||||
|
skip_serializing.set_true();
|
||||||
|
skip_deserializing.set_true();
|
||||||
|
}
|
||||||
|
|
||||||
// Parse `#[serde(skip_deserializing)]`
|
// Parse `#[serde(skip_deserializing)]`
|
||||||
Meta(Word(word)) if word == "skip_deserializing" => {
|
Meta(Word(word)) if word == "skip_deserializing" => {
|
||||||
skip_deserializing.set_true();
|
skip_deserializing.set_true();
|
||||||
@@ -595,6 +605,24 @@ impl Variant {
|
|||||||
other.set_true();
|
other.set_true();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse `#[serde(bound = "D: Serialize")]`
|
||||||
|
Meta(NameValue(ref m)) if m.ident == "bound" => {
|
||||||
|
if let Ok(where_predicates) =
|
||||||
|
parse_lit_into_where(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit)
|
||||||
|
{
|
||||||
|
ser_bound.set(where_predicates.clone());
|
||||||
|
de_bound.set(where_predicates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse `#[serde(bound(serialize = "D: Serialize", deserialize = "D: Deserialize"))]`
|
||||||
|
Meta(List(ref m)) if m.ident == "bound" => {
|
||||||
|
if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
|
||||||
|
ser_bound.set_opt(ser);
|
||||||
|
de_bound.set_opt(de);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse `#[serde(with = "...")]`
|
// Parse `#[serde(with = "...")]`
|
||||||
Meta(NameValue(ref m)) if m.ident == "with" => {
|
Meta(NameValue(ref m)) if m.ident == "with" => {
|
||||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||||
@@ -663,6 +691,8 @@ impl Variant {
|
|||||||
ser_renamed: ser_renamed,
|
ser_renamed: ser_renamed,
|
||||||
de_renamed: de_renamed,
|
de_renamed: de_renamed,
|
||||||
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
||||||
|
ser_bound: ser_bound.get(),
|
||||||
|
de_bound: de_bound.get(),
|
||||||
skip_deserializing: skip_deserializing.get(),
|
skip_deserializing: skip_deserializing.get(),
|
||||||
skip_serializing: skip_serializing.get(),
|
skip_serializing: skip_serializing.get(),
|
||||||
other: other.get(),
|
other: other.get(),
|
||||||
@@ -689,6 +719,14 @@ impl Variant {
|
|||||||
&self.rename_all
|
&self.rename_all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||||
|
self.ser_bound.as_ref().map(|vec| &vec[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||||
|
self.de_bound.as_ref().map(|vec| &vec[..])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn skip_deserializing(&self) -> bool {
|
pub fn skip_deserializing(&self) -> bool {
|
||||||
self.skip_deserializing
|
self.skip_deserializing
|
||||||
}
|
}
|
||||||
@@ -1000,7 +1038,7 @@ impl Field {
|
|||||||
};
|
};
|
||||||
deserialize_with.set_if_none(expr);
|
deserialize_with.set_if_none(expr);
|
||||||
}
|
}
|
||||||
} else if is_rptr(&field.ty, is_str) || is_rptr(&field.ty, is_slice_u8) {
|
} else if is_implicitly_borrowed(&field.ty) {
|
||||||
// Types &str and &[u8] are always implicitly borrowed. No need for
|
// Types &str and &[u8] are always implicitly borrowed. No need for
|
||||||
// a #[serde(borrow)].
|
// a #[serde(borrow)].
|
||||||
collect_lifetimes(&field.ty, &mut borrowed_lifetimes);
|
collect_lifetimes(&field.ty, &mut borrowed_lifetimes);
|
||||||
@@ -1262,6 +1300,14 @@ fn parse_lit_into_lifetimes(
|
|||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_implicitly_borrowed(ty: &syn::Type) -> bool {
|
||||||
|
is_implicitly_borrowed_reference(ty) || is_option(ty, is_implicitly_borrowed_reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_implicitly_borrowed_reference(ty: &syn::Type) -> bool {
|
||||||
|
is_reference(ty, is_str) || is_reference(ty, is_slice_u8)
|
||||||
|
}
|
||||||
|
|
||||||
// Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T".
|
// Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T".
|
||||||
// This can have false negatives and false positives.
|
// This can have false negatives and false positives.
|
||||||
//
|
//
|
||||||
@@ -1309,6 +1355,31 @@ fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_option(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||||
|
let path = match *ty {
|
||||||
|
syn::Type::Path(ref ty) => &ty.path,
|
||||||
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let seg = match path.segments.last() {
|
||||||
|
Some(seg) => seg.into_value(),
|
||||||
|
None => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let args = match seg.arguments {
|
||||||
|
syn::PathArguments::AngleBracketed(ref bracketed) => &bracketed.args,
|
||||||
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
seg.ident == "Option" && args.len() == 1 && match args[0] {
|
||||||
|
syn::GenericArgument::Type(ref arg) => elem(arg),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Whether the type looks like it might be `&T` where elem="T". This can have
|
// Whether the type looks like it might be `&T` where elem="T". This can have
|
||||||
// false negatives and false positives.
|
// false negatives and false positives.
|
||||||
//
|
//
|
||||||
@@ -1329,7 +1400,7 @@ fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
|||||||
// struct S<'a> {
|
// struct S<'a> {
|
||||||
// r: &'a str,
|
// r: &'a str,
|
||||||
// }
|
// }
|
||||||
fn is_rptr(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
fn is_reference(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||||
match *ty {
|
match *ty {
|
||||||
syn::Type::Reference(ref ty) => ty.mutability.is_none() && elem(&ty.elem),
|
syn::Type::Reference(ref ty) => ty.mutability.is_none() && elem(&ty.elem),
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
//!
|
//!
|
||||||
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
|
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.49")]
|
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.54")]
|
||||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||||
// Whitelisted clippy lints
|
// Whitelisted clippy lints
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
|
|||||||
+51
-9
@@ -138,6 +138,9 @@ fn build_generics(cont: &Container) -> syn::Generics {
|
|||||||
let generics =
|
let generics =
|
||||||
bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound);
|
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() {
|
match cont.attrs.ser_bound() {
|
||||||
Some(predicates) => bound::with_where_predicates(&generics, predicates),
|
Some(predicates) => bound::with_where_predicates(&generics, predicates),
|
||||||
None => bound::with_bound(
|
None => bound::with_bound(
|
||||||
@@ -150,13 +153,16 @@ fn build_generics(cont: &Container) -> syn::Generics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fields with a `skip_serializing` or `serialize_with` attribute, or which
|
// Fields with a `skip_serializing` or `serialize_with` attribute, or which
|
||||||
// belong to a variant with a `serialize_with` attribute, are not serialized by
|
// belong to a variant with a 'skip_serializing` or `serialize_with` attribute,
|
||||||
// us so we do not generate a bound. Fields with a `bound` attribute specify
|
// are not serialized by us so we do not generate a bound. Fields with a `bound`
|
||||||
// their own bound so we do not generate one. All other fields may need a `T:
|
// attribute specify their own bound so we do not generate one. All other fields
|
||||||
// Serialize` bound where T is the type of the field.
|
// 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 {
|
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()
|
!field.skip_serializing() && field.serialize_with().is_none() && field.ser_bound().is_none()
|
||||||
&& variant.map_or(true, |variant| variant.serialize_with().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 {
|
fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
||||||
@@ -237,8 +243,28 @@ fn serialize_tuple_struct(
|
|||||||
serialize_tuple_struct_visitor(fields, params, false, &TupleTrait::SerializeTupleStruct);
|
serialize_tuple_struct_visitor(fields, params, false, &TupleTrait::SerializeTupleStruct);
|
||||||
|
|
||||||
let type_name = cattrs.name().serialize_name();
|
let type_name = cattrs.name().serialize_name();
|
||||||
let len = serialize_stmts.len();
|
|
||||||
let let_mut = mut_if(len > 0);
|
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! {
|
quote_block! {
|
||||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_struct(__serializer, #type_name, #len));
|
let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_struct(__serializer, #type_name, #len));
|
||||||
@@ -737,8 +763,23 @@ fn serialize_tuple_variant(
|
|||||||
|
|
||||||
let serialize_stmts = serialize_tuple_struct_visitor(fields, params, true, &tuple_trait);
|
let serialize_stmts = serialize_tuple_struct_visitor(fields, params, true, &tuple_trait);
|
||||||
|
|
||||||
let len = serialize_stmts.len();
|
let mut serialized_fields = fields
|
||||||
let let_mut = mut_if(len > 0);
|
.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 {
|
match context {
|
||||||
TupleVariant::ExternallyTagged {
|
TupleVariant::ExternallyTagged {
|
||||||
@@ -960,6 +1001,7 @@ fn serialize_tuple_struct_visitor(
|
|||||||
fields
|
fields
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
.filter(|&(_, ref field)| !field.attrs.skip_serializing())
|
||||||
.map(|(i, field)| {
|
.map(|(i, field)| {
|
||||||
let mut field_expr = if is_enum {
|
let mut field_expr = if is_enum {
|
||||||
let id = Ident::new(&format!("__field{}", i), Span::call_site());
|
let id = Ident::new(&format!("__field{}", i), Span::call_site());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_test"
|
name = "serde_test"
|
||||||
version = "1.0.49" # remember to update html_root_url
|
version = "1.0.54" # remember to update html_root_url
|
||||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
description = "Token De/Serializer for testing De/Serialize implementations"
|
description = "Token De/Serializer for testing De/Serialize implementations"
|
||||||
|
|||||||
@@ -161,7 +161,7 @@
|
|||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.49")]
|
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.54")]
|
||||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||||
// Whitelisted clippy lints
|
// Whitelisted clippy lints
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
|
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2018 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(Deserialize)] //~ ERROR: proc-macro derive panicked
|
||||||
|
struct S<'de> {
|
||||||
|
s: &'de str, //~^^ HELP: cannot deserialize when there is a lifetime parameter called 'de
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2018 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(Deserialize)] //~ ERROR: proc-macro derive panicked
|
||||||
|
struct S {
|
||||||
|
string: String,
|
||||||
|
slice: [u8], //~^^^ HELP: cannot deserialize a dynamically sized struct
|
||||||
|
}
|
||||||
@@ -657,6 +657,44 @@ fn test_skip_serializing_struct() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
|
struct SkipSerializingTupleStruct<'a, B, C>(
|
||||||
|
&'a i8,
|
||||||
|
#[serde(skip_serializing)] B,
|
||||||
|
#[serde(skip_serializing_if = "ShouldSkip::should_skip")] C,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
C: ShouldSkip;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_skip_serializing_tuple_struct() {
|
||||||
|
let a = 1;
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingTupleStruct(&a, 2, 3),
|
||||||
|
&[
|
||||||
|
Token::TupleStruct {
|
||||||
|
name: "SkipSerializingTupleStruct",
|
||||||
|
len: 2,
|
||||||
|
},
|
||||||
|
Token::I8(1),
|
||||||
|
Token::I32(3),
|
||||||
|
Token::TupleStructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingTupleStruct(&a, 2, 123),
|
||||||
|
&[
|
||||||
|
Token::TupleStruct {
|
||||||
|
name: "SkipSerializingTupleStruct",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::I8(1),
|
||||||
|
Token::TupleStructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
struct SkipStruct<B> {
|
struct SkipStruct<B> {
|
||||||
a: i8,
|
a: i8,
|
||||||
@@ -705,6 +743,11 @@ where
|
|||||||
#[serde(skip_serializing_if = "ShouldSkip::should_skip")]
|
#[serde(skip_serializing_if = "ShouldSkip::should_skip")]
|
||||||
c: C,
|
c: C,
|
||||||
},
|
},
|
||||||
|
Tuple(
|
||||||
|
&'a i8,
|
||||||
|
#[serde(skip_serializing)] B,
|
||||||
|
#[serde(skip_serializing_if = "ShouldSkip::should_skip")] C,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -743,6 +786,33 @@ fn test_skip_serializing_enum() {
|
|||||||
Token::StructVariantEnd,
|
Token::StructVariantEnd,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingEnum::Tuple(&a, 2, 3),
|
||||||
|
&[
|
||||||
|
Token::TupleVariant {
|
||||||
|
name: "SkipSerializingEnum",
|
||||||
|
variant: "Tuple",
|
||||||
|
len: 2,
|
||||||
|
},
|
||||||
|
Token::I8(1),
|
||||||
|
Token::I32(3),
|
||||||
|
Token::TupleVariantEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingEnum::Tuple(&a, 2, 123),
|
||||||
|
&[
|
||||||
|
Token::TupleVariant {
|
||||||
|
name: "SkipSerializingEnum",
|
||||||
|
variant: "Tuple",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::I8(1),
|
||||||
|
Token::TupleVariantEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -1230,7 +1300,7 @@ fn test_invalid_length_enum() {
|
|||||||
Token::I32(1),
|
Token::I32(1),
|
||||||
Token::TupleVariantEnd,
|
Token::TupleVariantEnd,
|
||||||
],
|
],
|
||||||
"invalid length 1, expected tuple of 3 elements",
|
"invalid length 1, expected tuple variant InvalidLengthEnum::A with 3 elements",
|
||||||
);
|
);
|
||||||
assert_de_tokens_error::<InvalidLengthEnum>(
|
assert_de_tokens_error::<InvalidLengthEnum>(
|
||||||
&[
|
&[
|
||||||
@@ -1242,7 +1312,7 @@ fn test_invalid_length_enum() {
|
|||||||
Token::I32(1),
|
Token::I32(1),
|
||||||
Token::TupleVariantEnd,
|
Token::TupleVariantEnd,
|
||||||
],
|
],
|
||||||
"invalid length 1, expected tuple of 2 elements",
|
"invalid length 1, expected tuple variant InvalidLengthEnum::B with 2 elements",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1992,3 +2062,117 @@ fn test_untagged_enum_containing_flatten() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flatten_untagged_enum() {
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
struct Outer {
|
||||||
|
#[serde(flatten)]
|
||||||
|
inner: Inner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Inner {
|
||||||
|
Variant {
|
||||||
|
a: i32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = Outer {
|
||||||
|
inner: Inner::Variant {
|
||||||
|
a: 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&data,
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I32(0),
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flatten_option() {
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
struct Outer {
|
||||||
|
#[serde(flatten)]
|
||||||
|
inner1: Option<Inner1>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
inner2: Option<Inner2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
struct Inner1 {
|
||||||
|
inner1: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
struct Inner2 {
|
||||||
|
inner2: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Outer {
|
||||||
|
inner1: Some(Inner1 {
|
||||||
|
inner1: 1,
|
||||||
|
}),
|
||||||
|
inner2: Some(Inner2 {
|
||||||
|
inner2: 2,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::Str("inner1"),
|
||||||
|
Token::I32(1),
|
||||||
|
Token::Str("inner2"),
|
||||||
|
Token::I32(2),
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Outer {
|
||||||
|
inner1: Some(Inner1 {
|
||||||
|
inner1: 1,
|
||||||
|
}),
|
||||||
|
inner2: None,
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::Str("inner1"),
|
||||||
|
Token::I32(1),
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Outer {
|
||||||
|
inner1: None,
|
||||||
|
inner2: Some(Inner2 {
|
||||||
|
inner2: 2,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::Str("inner2"),
|
||||||
|
Token::I32(2),
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Outer {
|
||||||
|
inner1: None,
|
||||||
|
inner2: None,
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -216,6 +216,42 @@ fn test_gen() {
|
|||||||
}
|
}
|
||||||
assert::<WithTraits2<X, X>>();
|
assert::<WithTraits2<X, X>>();
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(bound = "D: SerializeWith + DeserializeWith")]
|
||||||
|
enum VariantWithTraits1<D, E> {
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "SerializeWith::serialize_with",
|
||||||
|
deserialize_with = "DeserializeWith::deserialize_with"
|
||||||
|
)]
|
||||||
|
D(D),
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "SerializeWith::serialize_with",
|
||||||
|
deserialize_with = "DeserializeWith::deserialize_with",
|
||||||
|
bound = "E: SerializeWith + DeserializeWith"
|
||||||
|
)]
|
||||||
|
E(E),
|
||||||
|
}
|
||||||
|
assert::<VariantWithTraits1<X, X>>();
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(bound(serialize = "D: SerializeWith", deserialize = "D: DeserializeWith"))]
|
||||||
|
enum VariantWithTraits2<D, E> {
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "SerializeWith::serialize_with",
|
||||||
|
deserialize_with = "DeserializeWith::deserialize_with"
|
||||||
|
)]
|
||||||
|
D(D),
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "SerializeWith::serialize_with", bound(serialize = "E: SerializeWith")
|
||||||
|
)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "DeserializeWith::deserialize_with",
|
||||||
|
bound(deserialize = "E: DeserializeWith")
|
||||||
|
)]
|
||||||
|
E(E),
|
||||||
|
}
|
||||||
|
assert::<VariantWithTraits2<X, X>>();
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct CowStr<'a>(Cow<'a, str>);
|
struct CowStr<'a>(Cow<'a, str>);
|
||||||
assert::<CowStr>();
|
assert::<CowStr>();
|
||||||
@@ -588,6 +624,25 @@ fn test_gen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "t", content = "c")]
|
||||||
|
enum AdjacentlyTaggedVoid {}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
enum SkippedVariant<T> {
|
||||||
|
#[serde(skip)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
T(T),
|
||||||
|
Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert::<SkippedVariant<X>>();
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ImpliciltyBorrowedOption<'a> {
|
||||||
|
option: std::option::Option<&'a str>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user