mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-07-22 09:05:45 +00:00
Merge pull request #2805 from Mingun/untagged-tests
Fix deserialization of empty structs and tuples in untagged enums
This commit is contained in:
+37
-161
@@ -1898,10 +1898,17 @@ mod content {
|
|||||||
where
|
where
|
||||||
V: Visitor<'de>,
|
V: Visitor<'de>,
|
||||||
{
|
{
|
||||||
|
// Covered by tests/test_enum_untagged.rs
|
||||||
|
// with_optional_field::*
|
||||||
match *self.content {
|
match *self.content {
|
||||||
Content::None => visitor.visit_none(),
|
Content::None => visitor.visit_none(),
|
||||||
Content::Some(ref v) => visitor.visit_some(ContentRefDeserializer::new(v)),
|
Content::Some(ref v) => visitor.visit_some(ContentRefDeserializer::new(v)),
|
||||||
Content::Unit => visitor.visit_unit(),
|
Content::Unit => visitor.visit_unit(),
|
||||||
|
// This case is necessary for formats which does not store marker of optionality of value,
|
||||||
|
// for example, JSON. When `deserialize_any` is requested from such formats, they will
|
||||||
|
// report value without using `Visitor::visit_some`, because they do not known in which
|
||||||
|
// contexts this value will be used.
|
||||||
|
// RON is example of format which preserve markers.
|
||||||
_ => visitor.visit_some(self),
|
_ => visitor.visit_some(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1931,10 +1938,17 @@ mod content {
|
|||||||
where
|
where
|
||||||
V: Visitor<'de>,
|
V: Visitor<'de>,
|
||||||
{
|
{
|
||||||
|
// Covered by tests/test_enum_untagged.rs
|
||||||
|
// newtype_struct
|
||||||
match *self.content {
|
match *self.content {
|
||||||
Content::Newtype(ref v) => {
|
Content::Newtype(ref v) => {
|
||||||
visitor.visit_newtype_struct(ContentRefDeserializer::new(v))
|
visitor.visit_newtype_struct(ContentRefDeserializer::new(v))
|
||||||
}
|
}
|
||||||
|
// This case is necessary for formats which does not store marker of a newtype,
|
||||||
|
// for example, JSON. When `deserialize_any` is requested from such formats, they will
|
||||||
|
// report value without using `Visitor::visit_newtype_struct`, because they do not
|
||||||
|
// known in which contexts this value will be used.
|
||||||
|
// RON is example of format which preserve markers.
|
||||||
_ => visitor.visit_newtype_struct(self),
|
_ => visitor.visit_newtype_struct(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2139,6 +2153,10 @@ mod content {
|
|||||||
fn unit_variant(self) -> Result<(), E> {
|
fn unit_variant(self) -> Result<(), E> {
|
||||||
match self.value {
|
match self.value {
|
||||||
Some(value) => de::Deserialize::deserialize(ContentRefDeserializer::new(value)),
|
Some(value) => de::Deserialize::deserialize(ContentRefDeserializer::new(value)),
|
||||||
|
// Covered by tests/test_annotations.rs
|
||||||
|
// test_partially_untagged_adjacently_tagged_enum
|
||||||
|
// Covered by tests/test_enum_untagged.rs
|
||||||
|
// newtype_enum::unit
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2148,6 +2166,11 @@ mod content {
|
|||||||
T: de::DeserializeSeed<'de>,
|
T: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
match self.value {
|
match self.value {
|
||||||
|
// Covered by tests/test_annotations.rs
|
||||||
|
// test_partially_untagged_enum_desugared
|
||||||
|
// test_partially_untagged_enum_generic
|
||||||
|
// Covered by tests/test_enum_untagged.rs
|
||||||
|
// newtype_enum::newtype
|
||||||
Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
|
Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
|
||||||
None => Err(de::Error::invalid_type(
|
None => Err(de::Error::invalid_type(
|
||||||
de::Unexpected::UnitVariant,
|
de::Unexpected::UnitVariant,
|
||||||
@@ -2161,9 +2184,13 @@ mod content {
|
|||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.value {
|
match self.value {
|
||||||
Some(Content::Seq(v)) => {
|
// Covered by tests/test_annotations.rs
|
||||||
de::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor)
|
// test_partially_untagged_enum
|
||||||
}
|
// test_partially_untagged_enum_desugared
|
||||||
|
// Covered by tests/test_enum_untagged.rs
|
||||||
|
// newtype_enum::tuple0
|
||||||
|
// newtype_enum::tuple2
|
||||||
|
Some(Content::Seq(v)) => visit_content_seq_ref(v, visitor),
|
||||||
Some(other) => Err(de::Error::invalid_type(
|
Some(other) => Err(de::Error::invalid_type(
|
||||||
other.unexpected(),
|
other.unexpected(),
|
||||||
&"tuple variant",
|
&"tuple variant",
|
||||||
@@ -2184,12 +2211,13 @@ mod content {
|
|||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.value {
|
match self.value {
|
||||||
Some(Content::Map(v)) => {
|
// Covered by tests/test_enum_untagged.rs
|
||||||
de::Deserializer::deserialize_any(MapRefDeserializer::new(v), visitor)
|
// newtype_enum::struct_from_map
|
||||||
}
|
Some(Content::Map(v)) => visit_content_map_ref(v, visitor),
|
||||||
Some(Content::Seq(v)) => {
|
// Covered by tests/test_enum_untagged.rs
|
||||||
de::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor)
|
// newtype_enum::struct_from_seq
|
||||||
}
|
// newtype_enum::empty_struct_from_seq
|
||||||
|
Some(Content::Seq(v)) => visit_content_seq_ref(v, visitor),
|
||||||
Some(other) => Err(de::Error::invalid_type(
|
Some(other) => Err(de::Error::invalid_type(
|
||||||
other.unexpected(),
|
other.unexpected(),
|
||||||
&"struct variant",
|
&"struct variant",
|
||||||
@@ -2202,158 +2230,6 @@ mod content {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SeqRefDeserializer<'a, 'de: 'a, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
iter: <&'a [Content<'de>] as IntoIterator>::IntoIter,
|
|
||||||
err: PhantomData<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de, E> SeqRefDeserializer<'a, 'de, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
fn new(slice: &'a [Content<'de>]) -> Self {
|
|
||||||
SeqRefDeserializer {
|
|
||||||
iter: slice.iter(),
|
|
||||||
err: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, E> de::Deserializer<'de> for SeqRefDeserializer<'a, 'de, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
type Error = E;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn deserialize_any<V>(mut self, visitor: V) -> Result<V::Value, Self::Error>
|
|
||||||
where
|
|
||||||
V: de::Visitor<'de>,
|
|
||||||
{
|
|
||||||
let len = self.iter.len();
|
|
||||||
if len == 0 {
|
|
||||||
visitor.visit_unit()
|
|
||||||
} else {
|
|
||||||
let ret = tri!(visitor.visit_seq(&mut self));
|
|
||||||
let remaining = self.iter.len();
|
|
||||||
if remaining == 0 {
|
|
||||||
Ok(ret)
|
|
||||||
} else {
|
|
||||||
Err(de::Error::invalid_length(len, &"fewer elements in array"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_to_deserialize_any! {
|
|
||||||
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
|
|
||||||
bytes byte_buf option unit unit_struct newtype_struct seq tuple
|
|
||||||
tuple_struct map struct enum identifier ignored_any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, E> de::SeqAccess<'de> for SeqRefDeserializer<'a, 'de, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
type Error = E;
|
|
||||||
|
|
||||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
|
|
||||||
where
|
|
||||||
T: de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
match self.iter.next() {
|
|
||||||
Some(value) => seed
|
|
||||||
.deserialize(ContentRefDeserializer::new(value))
|
|
||||||
.map(Some),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> Option<usize> {
|
|
||||||
size_hint::from_bounds(&self.iter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MapRefDeserializer<'a, 'de: 'a, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
iter: <&'a [(Content<'de>, Content<'de>)] as IntoIterator>::IntoIter,
|
|
||||||
value: Option<&'a Content<'de>>,
|
|
||||||
err: PhantomData<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de, E> MapRefDeserializer<'a, 'de, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
fn new(map: &'a [(Content<'de>, Content<'de>)]) -> Self {
|
|
||||||
MapRefDeserializer {
|
|
||||||
iter: map.iter(),
|
|
||||||
value: None,
|
|
||||||
err: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, E> de::MapAccess<'de> for MapRefDeserializer<'a, 'de, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
type Error = E;
|
|
||||||
|
|
||||||
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
|
|
||||||
where
|
|
||||||
T: de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
match self.iter.next() {
|
|
||||||
Some((key, value)) => {
|
|
||||||
self.value = Some(value);
|
|
||||||
seed.deserialize(ContentRefDeserializer::new(key)).map(Some)
|
|
||||||
}
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, Self::Error>
|
|
||||||
where
|
|
||||||
T: de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
match self.value.take() {
|
|
||||||
Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
|
|
||||||
None => Err(de::Error::custom("value is missing")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> Option<usize> {
|
|
||||||
size_hint::from_bounds(&self.iter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, E> de::Deserializer<'de> for MapRefDeserializer<'a, 'de, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
type Error = E;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
||||||
where
|
|
||||||
V: de::Visitor<'de>,
|
|
||||||
{
|
|
||||||
visitor.visit_map(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_to_deserialize_any! {
|
|
||||||
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
|
|
||||||
bytes byte_buf option unit unit_struct newtype_struct seq tuple
|
|
||||||
tuple_struct map struct enum identifier ignored_any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, E> de::IntoDeserializer<'de, E> for ContentDeserializer<'de, E>
|
impl<'de, E> de::IntoDeserializer<'de, E> for ContentDeserializer<'de, E>
|
||||||
where
|
where
|
||||||
E: de::Error,
|
E: de::Error,
|
||||||
|
|||||||
@@ -1895,18 +1895,6 @@ fn test_expecting_message_externally_tagged_enum() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_expecting_message_untagged_tagged_enum() {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
#[serde(expecting = "something strange...")]
|
|
||||||
enum Enum {
|
|
||||||
Untagged,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_de_tokens_error::<Enum>(&[Token::Str("Untagged")], "something strange...");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_expecting_message_identifier_enum() {
|
fn test_expecting_message_identifier_enum() {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -2958,41 +2946,6 @@ mod flatten {
|
|||||||
mod untagged {
|
mod untagged {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn straightforward() {
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum Data {
|
|
||||||
A {
|
|
||||||
a: i32,
|
|
||||||
#[serde(flatten)]
|
|
||||||
flat: Flat,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|
||||||
struct Flat {
|
|
||||||
b: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = Data::A {
|
|
||||||
a: 0,
|
|
||||||
flat: Flat { b: 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&data,
|
|
||||||
&[
|
|
||||||
Token::Map { len: None },
|
|
||||||
Token::Str("a"),
|
|
||||||
Token::I32(0),
|
|
||||||
Token::Str("b"),
|
|
||||||
Token::I32(0),
|
|
||||||
Token::MapEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
struct Flatten {
|
struct Flatten {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
|||||||
@@ -0,0 +1,583 @@
|
|||||||
|
#![deny(trivial_numeric_casts)]
|
||||||
|
#![allow(
|
||||||
|
clippy::derive_partial_eq_without_eq,
|
||||||
|
clippy::enum_variant_names,
|
||||||
|
clippy::redundant_field_names,
|
||||||
|
clippy::too_many_lines
|
||||||
|
)]
|
||||||
|
|
||||||
|
mod bytes;
|
||||||
|
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex() {
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Untagged {
|
||||||
|
A { a: u8 },
|
||||||
|
B { b: u8 },
|
||||||
|
C,
|
||||||
|
D(u8),
|
||||||
|
E(String),
|
||||||
|
F(u8, u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Untagged::A { a: 1 },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::U8(1),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Untagged::B { b: 2 },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("b"),
|
||||||
|
Token::U8(2),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Serializes to unit, deserializes from either depending on format's
|
||||||
|
// preference.
|
||||||
|
assert_tokens(&Untagged::C, &[Token::Unit]);
|
||||||
|
assert_de_tokens(&Untagged::C, &[Token::None]);
|
||||||
|
|
||||||
|
assert_tokens(&Untagged::D(4), &[Token::U8(4)]);
|
||||||
|
assert_tokens(&Untagged::E("e".to_owned()), &[Token::Str("e")]);
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Untagged::F(1, 2),
|
||||||
|
&[
|
||||||
|
Token::Tuple { len: 2 },
|
||||||
|
Token::U8(1),
|
||||||
|
Token::U8(2),
|
||||||
|
Token::TupleEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens_error::<Untagged>(
|
||||||
|
&[Token::Tuple { len: 1 }, Token::U8(1), Token::TupleEnd],
|
||||||
|
"data did not match any variant of untagged enum Untagged",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens_error::<Untagged>(
|
||||||
|
&[
|
||||||
|
Token::Tuple { len: 3 },
|
||||||
|
Token::U8(1),
|
||||||
|
Token::U8(2),
|
||||||
|
Token::U8(3),
|
||||||
|
Token::TupleEnd,
|
||||||
|
],
|
||||||
|
"data did not match any variant of untagged enum Untagged",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn newtype_unit_and_empty_map() {
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
struct Unit;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Message {
|
||||||
|
Unit(Unit),
|
||||||
|
Map(BTreeMap<String, String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Message::Map(BTreeMap::new()),
|
||||||
|
&[Token::Map { len: Some(0) }, Token::MapEnd],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::ContentRefDeserializer::deserialize_newtype_struct
|
||||||
|
#[test]
|
||||||
|
fn newtype_struct() {
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
struct NewtypeStruct(u32);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum E {
|
||||||
|
Newtype(NewtypeStruct),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = E::Newtype(NewtypeStruct(5));
|
||||||
|
|
||||||
|
// Content::Newtype case
|
||||||
|
assert_tokens(
|
||||||
|
&value,
|
||||||
|
&[
|
||||||
|
Token::NewtypeStruct {
|
||||||
|
name: "NewtypeStruct",
|
||||||
|
},
|
||||||
|
Token::U32(5),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// _ case
|
||||||
|
assert_de_tokens(&value, &[Token::U32(5)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod newtype_enum {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Outer {
|
||||||
|
Inner(Inner),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
enum Inner {
|
||||||
|
Unit,
|
||||||
|
Newtype(u8),
|
||||||
|
Tuple0(),
|
||||||
|
Tuple2(u8, u8),
|
||||||
|
Struct { f: u8 },
|
||||||
|
EmptyStruct {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::unit_variant
|
||||||
|
#[test]
|
||||||
|
fn unit() {
|
||||||
|
assert_tokens(
|
||||||
|
&Outer::Inner(Inner::Unit),
|
||||||
|
&[Token::UnitVariant {
|
||||||
|
name: "Inner",
|
||||||
|
variant: "Unit",
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::newtype_variant_seed
|
||||||
|
#[test]
|
||||||
|
fn newtype() {
|
||||||
|
assert_tokens(
|
||||||
|
&Outer::Inner(Inner::Newtype(1)),
|
||||||
|
&[
|
||||||
|
Token::NewtypeVariant {
|
||||||
|
name: "Inner",
|
||||||
|
variant: "Newtype",
|
||||||
|
},
|
||||||
|
Token::U8(1),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::tuple_variant
|
||||||
|
#[test]
|
||||||
|
fn tuple0() {
|
||||||
|
assert_tokens(
|
||||||
|
&Outer::Inner(Inner::Tuple0()),
|
||||||
|
&[
|
||||||
|
Token::TupleVariant {
|
||||||
|
name: "Inner",
|
||||||
|
variant: "Tuple0",
|
||||||
|
len: 0,
|
||||||
|
},
|
||||||
|
Token::TupleVariantEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::tuple_variant
|
||||||
|
#[test]
|
||||||
|
fn tuple2() {
|
||||||
|
assert_tokens(
|
||||||
|
&Outer::Inner(Inner::Tuple2(1, 1)),
|
||||||
|
&[
|
||||||
|
Token::TupleVariant {
|
||||||
|
name: "Inner",
|
||||||
|
variant: "Tuple2",
|
||||||
|
len: 2,
|
||||||
|
},
|
||||||
|
Token::U8(1),
|
||||||
|
Token::U8(1),
|
||||||
|
Token::TupleVariantEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
|
||||||
|
// Content::Map case
|
||||||
|
#[test]
|
||||||
|
fn struct_from_map() {
|
||||||
|
assert_tokens(
|
||||||
|
&Outer::Inner(Inner::Struct { f: 1 }),
|
||||||
|
&[
|
||||||
|
Token::StructVariant {
|
||||||
|
name: "Inner",
|
||||||
|
variant: "Struct",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("f"),
|
||||||
|
Token::U8(1),
|
||||||
|
Token::StructVariantEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
|
||||||
|
// Content::Seq case
|
||||||
|
#[test]
|
||||||
|
fn struct_from_seq() {
|
||||||
|
assert_de_tokens(
|
||||||
|
&Outer::Inner(Inner::Struct { f: 1 }),
|
||||||
|
&[
|
||||||
|
Token::Map { len: Some(1) },
|
||||||
|
// tag
|
||||||
|
Token::Str("Struct"),
|
||||||
|
// content
|
||||||
|
Token::Seq { len: Some(1) },
|
||||||
|
Token::U8(1),
|
||||||
|
Token::SeqEnd,
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
|
||||||
|
// Content::Map case
|
||||||
|
// Special case - empty map
|
||||||
|
#[test]
|
||||||
|
fn empty_struct_from_map() {
|
||||||
|
assert_de_tokens(
|
||||||
|
&Outer::Inner(Inner::EmptyStruct {}),
|
||||||
|
&[
|
||||||
|
Token::Map { len: Some(1) },
|
||||||
|
// tag
|
||||||
|
Token::Str("EmptyStruct"),
|
||||||
|
// content
|
||||||
|
Token::Map { len: Some(0) },
|
||||||
|
Token::MapEnd,
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
|
||||||
|
// Content::Seq case
|
||||||
|
// Special case - empty seq
|
||||||
|
#[test]
|
||||||
|
fn empty_struct_from_seq() {
|
||||||
|
assert_de_tokens(
|
||||||
|
&Outer::Inner(Inner::EmptyStruct {}),
|
||||||
|
&[
|
||||||
|
Token::Map { len: Some(1) },
|
||||||
|
// tag
|
||||||
|
Token::Str("EmptyStruct"),
|
||||||
|
// content
|
||||||
|
Token::Seq { len: Some(0) },
|
||||||
|
Token::SeqEnd,
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaches crate::private::de::content::ContentRefDeserializer::deserialize_option
|
||||||
|
mod with_optional_field {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Enum {
|
||||||
|
Struct { optional: Option<u32> },
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn some() {
|
||||||
|
assert_tokens(
|
||||||
|
&Enum::Struct { optional: Some(42) },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Enum",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("optional"),
|
||||||
|
Token::Some,
|
||||||
|
Token::U32(42),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn some_without_marker() {
|
||||||
|
assert_de_tokens(
|
||||||
|
&Enum::Struct { optional: Some(42) },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Enum",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("optional"),
|
||||||
|
Token::U32(42),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn none() {
|
||||||
|
assert_tokens(
|
||||||
|
&Enum::Struct { optional: None },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Enum",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("optional"),
|
||||||
|
Token::None,
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unit() {
|
||||||
|
assert_de_tokens(
|
||||||
|
&Enum::Struct { optional: None },
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::Str("optional"),
|
||||||
|
Token::Unit,
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_and_bytes() {
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Untagged {
|
||||||
|
String {
|
||||||
|
string: String,
|
||||||
|
},
|
||||||
|
Bytes {
|
||||||
|
#[serde(with = "bytes")]
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::String {
|
||||||
|
string: "\0".to_owned(),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("string"),
|
||||||
|
Token::Str("\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::String {
|
||||||
|
string: "\0".to_owned(),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("string"),
|
||||||
|
Token::String("\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::String {
|
||||||
|
string: "\0".to_owned(),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("string"),
|
||||||
|
Token::Bytes(b"\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::String {
|
||||||
|
string: "\0".to_owned(),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("string"),
|
||||||
|
Token::ByteBuf(b"\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::Bytes { bytes: vec![0] },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("bytes"),
|
||||||
|
Token::Str("\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::Bytes { bytes: vec![0] },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("bytes"),
|
||||||
|
Token::String("\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::Bytes { bytes: vec![0] },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("bytes"),
|
||||||
|
Token::Bytes(b"\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::Bytes { bytes: vec![0] },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("bytes"),
|
||||||
|
Token::ByteBuf(b"\0"),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Untagged::Bytes { bytes: vec![0] },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Untagged",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("bytes"),
|
||||||
|
Token::Seq { len: Some(1) },
|
||||||
|
Token::U8(0),
|
||||||
|
Token::SeqEnd,
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn contains_flatten() {
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum Data {
|
||||||
|
A {
|
||||||
|
a: i32,
|
||||||
|
#[serde(flatten)]
|
||||||
|
flat: Flat,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
struct Flat {
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = Data::A {
|
||||||
|
a: 0,
|
||||||
|
flat: Flat { b: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&data,
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I32(0),
|
||||||
|
Token::Str("b"),
|
||||||
|
Token::I32(0),
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn contains_flatten_with_integer_key() {
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Untagged {
|
||||||
|
Variant {
|
||||||
|
#[serde(flatten)]
|
||||||
|
map: BTreeMap<u64, String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Untagged::Variant {
|
||||||
|
map: {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
map.insert(100, "BTreeMap".to_owned());
|
||||||
|
map
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::Map { len: None },
|
||||||
|
Token::U64(100),
|
||||||
|
Token::Str("BTreeMap"),
|
||||||
|
Token::MapEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expecting_message() {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[serde(expecting = "something strange...")]
|
||||||
|
enum Enum {
|
||||||
|
Untagged,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_de_tokens_error::<Enum>(&[Token::Str("Untagged")], "something strange...");
|
||||||
|
}
|
||||||
@@ -6,13 +6,8 @@
|
|||||||
clippy::too_many_lines
|
clippy::too_many_lines
|
||||||
)]
|
)]
|
||||||
|
|
||||||
mod bytes;
|
|
||||||
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use serde_test::{
|
use serde_test::{assert_de_tokens, assert_ser_tokens, assert_tokens, Token};
|
||||||
assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, assert_tokens, Token,
|
|
||||||
};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
// That tests that the derived Serialize implementation doesn't trigger
|
// That tests that the derived Serialize implementation doesn't trigger
|
||||||
@@ -433,26 +428,6 @@ fn test_generic_newtype_struct() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_untagged_newtype_struct() {
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum E {
|
|
||||||
Newtype(GenericNewTypeStruct<u32>),
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&E::Newtype(GenericNewTypeStruct(5u32)),
|
|
||||||
&[
|
|
||||||
Token::NewtypeStruct {
|
|
||||||
name: "GenericNewTypeStruct",
|
|
||||||
},
|
|
||||||
Token::U32(5),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generic_tuple_struct() {
|
fn test_generic_tuple_struct() {
|
||||||
assert_tokens(
|
assert_tokens(
|
||||||
@@ -577,80 +552,6 @@ fn test_enum_state_field() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_untagged_enum() {
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum Untagged {
|
|
||||||
A { a: u8 },
|
|
||||||
B { b: u8 },
|
|
||||||
C,
|
|
||||||
D(u8),
|
|
||||||
E(String),
|
|
||||||
F(u8, u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Untagged::A { a: 1 },
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("a"),
|
|
||||||
Token::U8(1),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Untagged::B { b: 2 },
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("b"),
|
|
||||||
Token::U8(2),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Serializes to unit, deserializes from either depending on format's
|
|
||||||
// preference.
|
|
||||||
assert_tokens(&Untagged::C, &[Token::Unit]);
|
|
||||||
assert_de_tokens(&Untagged::C, &[Token::None]);
|
|
||||||
|
|
||||||
assert_tokens(&Untagged::D(4), &[Token::U8(4)]);
|
|
||||||
assert_tokens(&Untagged::E("e".to_owned()), &[Token::Str("e")]);
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Untagged::F(1, 2),
|
|
||||||
&[
|
|
||||||
Token::Tuple { len: 2 },
|
|
||||||
Token::U8(1),
|
|
||||||
Token::U8(2),
|
|
||||||
Token::TupleEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens_error::<Untagged>(
|
|
||||||
&[Token::Tuple { len: 1 }, Token::U8(1), Token::TupleEnd],
|
|
||||||
"data did not match any variant of untagged enum Untagged",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens_error::<Untagged>(
|
|
||||||
&[
|
|
||||||
Token::Tuple { len: 3 },
|
|
||||||
Token::U8(1),
|
|
||||||
Token::U8(2),
|
|
||||||
Token::U8(3),
|
|
||||||
Token::TupleEnd,
|
|
||||||
],
|
|
||||||
"data did not match any variant of untagged enum Untagged",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_internally_tagged_struct() {
|
fn test_internally_tagged_struct() {
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
@@ -750,240 +651,6 @@ fn test_internally_tagged_struct_with_flattened_field() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_untagged_enum_with_flattened_integer_key() {
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum Untagged {
|
|
||||||
Variant {
|
|
||||||
#[serde(flatten)]
|
|
||||||
map: BTreeMap<u64, String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Untagged::Variant {
|
|
||||||
map: {
|
|
||||||
let mut map = BTreeMap::new();
|
|
||||||
map.insert(100, "BTreeMap".to_owned());
|
|
||||||
map
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&[
|
|
||||||
Token::Map { len: None },
|
|
||||||
Token::U64(100),
|
|
||||||
Token::Str("BTreeMap"),
|
|
||||||
Token::MapEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_enum_in_untagged_enum() {
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum Outer {
|
|
||||||
Inner(Inner),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
enum Inner {
|
|
||||||
Unit,
|
|
||||||
Newtype(u8),
|
|
||||||
Tuple(u8, u8),
|
|
||||||
Struct { f: u8 },
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Outer::Inner(Inner::Unit),
|
|
||||||
&[Token::UnitVariant {
|
|
||||||
name: "Inner",
|
|
||||||
variant: "Unit",
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Outer::Inner(Inner::Newtype(1)),
|
|
||||||
&[
|
|
||||||
Token::NewtypeVariant {
|
|
||||||
name: "Inner",
|
|
||||||
variant: "Newtype",
|
|
||||||
},
|
|
||||||
Token::U8(1),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Outer::Inner(Inner::Tuple(1, 1)),
|
|
||||||
&[
|
|
||||||
Token::TupleVariant {
|
|
||||||
name: "Inner",
|
|
||||||
variant: "Tuple",
|
|
||||||
len: 2,
|
|
||||||
},
|
|
||||||
Token::U8(1),
|
|
||||||
Token::U8(1),
|
|
||||||
Token::TupleVariantEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Outer::Inner(Inner::Struct { f: 1 }),
|
|
||||||
&[
|
|
||||||
Token::StructVariant {
|
|
||||||
name: "Inner",
|
|
||||||
variant: "Struct",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("f"),
|
|
||||||
Token::U8(1),
|
|
||||||
Token::StructVariantEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_untagged_bytes() {
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum Untagged {
|
|
||||||
String {
|
|
||||||
string: String,
|
|
||||||
},
|
|
||||||
Bytes {
|
|
||||||
#[serde(with = "bytes")]
|
|
||||||
bytes: Vec<u8>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::String {
|
|
||||||
string: "\0".to_owned(),
|
|
||||||
},
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("string"),
|
|
||||||
Token::Str("\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::String {
|
|
||||||
string: "\0".to_owned(),
|
|
||||||
},
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("string"),
|
|
||||||
Token::String("\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::String {
|
|
||||||
string: "\0".to_owned(),
|
|
||||||
},
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("string"),
|
|
||||||
Token::Bytes(b"\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::String {
|
|
||||||
string: "\0".to_owned(),
|
|
||||||
},
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("string"),
|
|
||||||
Token::ByteBuf(b"\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::Bytes { bytes: vec![0] },
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("bytes"),
|
|
||||||
Token::Str("\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::Bytes { bytes: vec![0] },
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("bytes"),
|
|
||||||
Token::String("\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::Bytes { bytes: vec![0] },
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("bytes"),
|
|
||||||
Token::Bytes(b"\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::Bytes { bytes: vec![0] },
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("bytes"),
|
|
||||||
Token::ByteBuf(b"\0"),
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_de_tokens(
|
|
||||||
&Untagged::Bytes { bytes: vec![0] },
|
|
||||||
&[
|
|
||||||
Token::Struct {
|
|
||||||
name: "Untagged",
|
|
||||||
len: 1,
|
|
||||||
},
|
|
||||||
Token::Str("bytes"),
|
|
||||||
Token::Seq { len: Some(1) },
|
|
||||||
Token::U8(0),
|
|
||||||
Token::SeqEnd,
|
|
||||||
Token::StructEnd,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rename_all() {
|
fn test_rename_all() {
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
@@ -1167,24 +834,6 @@ fn test_rename_all_fields() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_untagged_newtype_variant_containing_unit_struct_not_map() {
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
struct Unit;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum Message {
|
|
||||||
Unit(Unit),
|
|
||||||
Map(BTreeMap<String, String>),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_tokens(
|
|
||||||
&Message::Map(BTreeMap::new()),
|
|
||||||
&[Token::Map { len: Some(0) }, Token::MapEnd],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packed_struct_can_derive_serialize() {
|
fn test_packed_struct_can_derive_serialize() {
|
||||||
#[derive(Copy, Clone, Serialize)]
|
#[derive(Copy, Clone, Serialize)]
|
||||||
|
|||||||
Reference in New Issue
Block a user