mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-25 15:07:56 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8fafc7420c | |||
| bea1c5b0f5 | |||
| aa37caf216 | |||
| 2440b59aae | |||
| 873cfbe9ab | |||
| c96efcb87a | |||
| b53026a21b |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.1" # remember to update html_root_url
|
version = "1.0.2" # 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"
|
||||||
|
|||||||
+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.1")]
|
#![doc(html_root_url = "https://docs.rs/serde/1.0.2")]
|
||||||
|
|
||||||
// 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)]
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use de::Unexpected;
|
|||||||
#[cfg(any(feature = "std", feature = "collections"))]
|
#[cfg(any(feature = "std", feature = "collections"))]
|
||||||
pub use self::content::{Content, ContentRefDeserializer, ContentDeserializer,
|
pub use self::content::{Content, ContentRefDeserializer, ContentDeserializer,
|
||||||
TaggedContentVisitor, TagOrContentField, TagOrContentFieldVisitor,
|
TaggedContentVisitor, TagOrContentField, TagOrContentFieldVisitor,
|
||||||
|
TagContentOtherField, TagContentOtherFieldVisitor,
|
||||||
InternallyTaggedUnitVisitor, UntaggedUnitVisitor};
|
InternallyTaggedUnitVisitor, UntaggedUnitVisitor};
|
||||||
|
|
||||||
/// If the missing field is of type `Option<T>` then treat is as `None`,
|
/// If the missing field is of type `Option<T>` then treat is as `None`,
|
||||||
@@ -863,6 +864,54 @@ mod content {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by generated code to deserialize an adjacently tagged enum when
|
||||||
|
/// ignoring unrelated fields is allowed.
|
||||||
|
///
|
||||||
|
/// Not public API.
|
||||||
|
pub enum TagContentOtherField {
|
||||||
|
Tag,
|
||||||
|
Content,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Not public API.
|
||||||
|
pub struct TagContentOtherFieldVisitor {
|
||||||
|
pub tag: &'static str,
|
||||||
|
pub content: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> DeserializeSeed<'de> for TagContentOtherFieldVisitor {
|
||||||
|
type Value = TagContentOtherField;
|
||||||
|
|
||||||
|
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for TagContentOtherFieldVisitor {
|
||||||
|
type Value = TagContentOtherField;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(formatter, "{:?}, {:?}, or other ignored fields", self.tag, self.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, field: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if field == self.tag {
|
||||||
|
Ok(TagContentOtherField::Tag)
|
||||||
|
} else if field == self.content {
|
||||||
|
Ok(TagContentOtherField::Content)
|
||||||
|
} else {
|
||||||
|
Ok(TagContentOtherField::Other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Not public API
|
/// Not public API
|
||||||
pub struct ContentDeserializer<E> {
|
pub struct ContentDeserializer<E> {
|
||||||
content: Content,
|
content: Content,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.1" # remember to update html_root_url
|
version = "1.0.2" # 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)]"
|
||||||
|
|||||||
+59
-12
@@ -795,9 +795,19 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
|
|
||||||
let expecting = format!("adjacently tagged enum {}", params.type_name());
|
let expecting = format!("adjacently tagged enum {}", params.type_name());
|
||||||
let type_name = cattrs.name().deserialize_name();
|
let type_name = cattrs.name().deserialize_name();
|
||||||
|
let deny_unknown_fields = cattrs.deny_unknown_fields();
|
||||||
|
|
||||||
|
/// If unknown fields are allowed, we pick the visitor that can
|
||||||
|
/// step over those. Otherwise we pick the visitor that fails on
|
||||||
|
/// unknown keys.
|
||||||
|
let field_visitor_ty = if deny_unknown_fields {
|
||||||
|
quote! { _serde::private::de::TagOrContentFieldVisitor }
|
||||||
|
} else {
|
||||||
|
quote! { _serde::private::de::TagContentOtherFieldVisitor }
|
||||||
|
};
|
||||||
|
|
||||||
let tag_or_content = quote! {
|
let tag_or_content = quote! {
|
||||||
_serde::private::de::TagOrContentFieldVisitor {
|
#field_visitor_ty {
|
||||||
tag: #tag,
|
tag: #tag,
|
||||||
content: #content,
|
content: #content,
|
||||||
}
|
}
|
||||||
@@ -844,9 +854,46 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let visit_third_key = quote! {
|
/// Advance the map by one key, returning early in case of error.
|
||||||
// Visit the third key in the map, hopefully there isn't one.
|
let next_key = quote! {
|
||||||
match try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content)) {
|
try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content))
|
||||||
|
};
|
||||||
|
|
||||||
|
/// When allowing unknown fields, we want to transparently step through keys we don't care
|
||||||
|
/// about until we find `tag`, `content`, or run out of keys.
|
||||||
|
let next_relevant_key = if deny_unknown_fields {
|
||||||
|
next_key
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
let mut __rk : _serde::export::Option<_serde::private::de::TagOrContentField> = _serde::export::None;
|
||||||
|
while let _serde::export::Some(__k) = #next_key {
|
||||||
|
match __k {
|
||||||
|
_serde::private::de::TagContentOtherField::Other => {
|
||||||
|
try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map));
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
_serde::private::de::TagContentOtherField::Tag => {
|
||||||
|
__rk = _serde::export::Some(_serde::private::de::TagOrContentField::Tag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_serde::private::de::TagContentOtherField::Content => {
|
||||||
|
__rk = _serde::export::Some(_serde::private::de::TagOrContentField::Content);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__rk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Step through remaining keys, looking for duplicates of previously-seen keys.
|
||||||
|
/// When unknown fields are denied, any key that isn't a duplicate will at this
|
||||||
|
/// point immediately produce an error.
|
||||||
|
let visit_remaining_keys = quote! {
|
||||||
|
match #next_relevant_key {
|
||||||
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
||||||
_serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
|
_serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
|
||||||
}
|
}
|
||||||
@@ -895,14 +942,14 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
|
fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
|
||||||
where __A: _serde::de::MapAccess<'de>
|
where __A: _serde::de::MapAccess<'de>
|
||||||
{
|
{
|
||||||
// Visit the first key.
|
// Visit the first relevant key.
|
||||||
match try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content)) {
|
match #next_relevant_key {
|
||||||
// First key is the tag.
|
// First key is the tag.
|
||||||
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
||||||
// Parse the tag.
|
// Parse the tag.
|
||||||
let __field = try!(_serde::de::MapAccess::next_value(&mut __map));
|
let __field = try!(_serde::de::MapAccess::next_value(&mut __map));
|
||||||
// Visit the second key.
|
// Visit the second key.
|
||||||
match try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content)) {
|
match #next_relevant_key {
|
||||||
// Second key is a duplicate of the tag.
|
// Second key is a duplicate of the tag.
|
||||||
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
||||||
_serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
|
_serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
|
||||||
@@ -915,8 +962,8 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
marker: _serde::export::PhantomData,
|
marker: _serde::export::PhantomData,
|
||||||
lifetime: _serde::export::PhantomData,
|
lifetime: _serde::export::PhantomData,
|
||||||
}));
|
}));
|
||||||
// Visit the third key, hopefully there isn't one.
|
// Visit remaining keys, looking for duplicates.
|
||||||
#visit_third_key
|
#visit_remaining_keys
|
||||||
}
|
}
|
||||||
// There is no second key; might be okay if the we have a unit variant.
|
// There is no second key; might be okay if the we have a unit variant.
|
||||||
_serde::export::None => #missing_content
|
_serde::export::None => #missing_content
|
||||||
@@ -927,7 +974,7 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
// Buffer up the content.
|
// Buffer up the content.
|
||||||
let __content = try!(_serde::de::MapAccess::next_value::<_serde::private::de::Content>(&mut __map));
|
let __content = try!(_serde::de::MapAccess::next_value::<_serde::private::de::Content>(&mut __map));
|
||||||
// Visit the second key.
|
// Visit the second key.
|
||||||
match try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content)) {
|
match #next_relevant_key {
|
||||||
// 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);
|
||||||
@@ -936,8 +983,8 @@ fn deserialize_adjacently_tagged_enum(
|
|||||||
// Deserialize the buffered content now that we know the variant.
|
// Deserialize the buffered content now that we know the variant.
|
||||||
#(#variant_arms)*
|
#(#variant_arms)*
|
||||||
});
|
});
|
||||||
// Visit the third key, hopefully there isn't one.
|
// Visit remaining keys, looking for duplicates.
|
||||||
#visit_third_key
|
#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) => {
|
||||||
|
|||||||
@@ -16,7 +16,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.1")]
|
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.2")]
|
||||||
|
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(used_underscore_binding))]
|
#![cfg_attr(feature = "cargo-clippy", allow(used_underscore_binding))]
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ pub enum Style {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Container<'a> {
|
impl<'a> Container<'a> {
|
||||||
pub fn from_ast(cx: &Ctxt, item: &'a syn::MacroInput) -> Container<'a> {
|
pub fn from_ast(cx: &Ctxt, item: &'a syn::DeriveInput) -> Container<'a> {
|
||||||
let attrs = attr::Container::from_ast(cx, item);
|
let attrs = attr::Container::from_ast(cx, item);
|
||||||
|
|
||||||
let mut body = match item.body {
|
let mut body = match item.body {
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ pub enum Identifier {
|
|||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
/// Extract out the `#[serde(...)]` attributes from an item.
|
/// Extract out the `#[serde(...)]` attributes from an item.
|
||||||
pub fn from_ast(cx: &Ctxt, item: &syn::MacroInput) -> Self {
|
pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self {
|
||||||
let mut ser_name = Attr::none(cx, "rename");
|
let mut ser_name = Attr::none(cx, "rename");
|
||||||
let mut de_name = Attr::none(cx, "rename");
|
let mut de_name = Attr::none(cx, "rename");
|
||||||
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
|
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
|
||||||
@@ -421,7 +421,7 @@ impl Container {
|
|||||||
|
|
||||||
fn decide_tag(
|
fn decide_tag(
|
||||||
cx: &Ctxt,
|
cx: &Ctxt,
|
||||||
item: &syn::MacroInput,
|
item: &syn::DeriveInput,
|
||||||
untagged: BoolAttr,
|
untagged: BoolAttr,
|
||||||
internal_tag: Attr<String>,
|
internal_tag: Attr<String>,
|
||||||
content: Attr<String>,
|
content: Attr<String>,
|
||||||
@@ -477,7 +477,7 @@ fn decide_tag(
|
|||||||
|
|
||||||
fn decide_identifier(
|
fn decide_identifier(
|
||||||
cx: &Ctxt,
|
cx: &Ctxt,
|
||||||
item: &syn::MacroInput,
|
item: &syn::DeriveInput,
|
||||||
field_identifier: BoolAttr,
|
field_identifier: BoolAttr,
|
||||||
variant_identifier: BoolAttr,
|
variant_identifier: BoolAttr,
|
||||||
) -> Identifier {
|
) -> Identifier {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_test"
|
name = "serde_test"
|
||||||
version = "1.0.1" # remember to update html_root_url
|
version = "1.0.2" # 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"
|
||||||
|
|||||||
@@ -155,7 +155,7 @@
|
|||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.1")]
|
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.2")]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|||||||
@@ -751,6 +751,31 @@ fn test_adjacently_tagged_enum() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// unit with excess content (f, g, h)
|
||||||
|
assert_de_tokens(
|
||||||
|
&AdjacentlyTagged::Unit::<u8>,
|
||||||
|
&[
|
||||||
|
Token::Struct { name: "AdjacentlyTagged", len: 3 },
|
||||||
|
|
||||||
|
Token::Str("f"),
|
||||||
|
Token::Unit,
|
||||||
|
|
||||||
|
Token::Str("t"),
|
||||||
|
Token::Str("Unit"),
|
||||||
|
|
||||||
|
Token::Str("g"),
|
||||||
|
Token::Unit,
|
||||||
|
|
||||||
|
Token::Str("c"),
|
||||||
|
Token::Unit,
|
||||||
|
|
||||||
|
Token::Str("h"),
|
||||||
|
Token::Unit,
|
||||||
|
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
// newtype with tag first
|
// newtype with tag first
|
||||||
assert_tokens(
|
assert_tokens(
|
||||||
&AdjacentlyTagged::Newtype::<u8>(1),
|
&AdjacentlyTagged::Newtype::<u8>(1),
|
||||||
@@ -860,6 +885,66 @@ fn test_adjacently_tagged_enum() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_adjacently_tagged_enum_deny_unknown_fields() {
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
#[serde(tag = "t", content = "c", deny_unknown_fields)]
|
||||||
|
enum AdjacentlyTagged {
|
||||||
|
Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&AdjacentlyTagged::Unit,
|
||||||
|
&[
|
||||||
|
Token::Struct { name: "AdjacentlyTagged", len: 2},
|
||||||
|
|
||||||
|
Token::Str("t"),
|
||||||
|
Token::Str("Unit"),
|
||||||
|
|
||||||
|
Token::Str("c"),
|
||||||
|
Token::Unit,
|
||||||
|
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens_error::<AdjacentlyTagged>(
|
||||||
|
&[
|
||||||
|
Token::Struct { name: "AdjacentlyTagged", len: 3},
|
||||||
|
|
||||||
|
Token::Str("t"),
|
||||||
|
Token::Str("Unit"),
|
||||||
|
|
||||||
|
Token::Str("c"),
|
||||||
|
Token::Unit,
|
||||||
|
|
||||||
|
Token::Str("h"),
|
||||||
|
],
|
||||||
|
r#"invalid value: string "h", expected "t" or "c""#
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens_error::<AdjacentlyTagged>(
|
||||||
|
&[
|
||||||
|
Token::Struct { name: "AdjacentlyTagged", len: 3},
|
||||||
|
|
||||||
|
Token::Str("h"),
|
||||||
|
],
|
||||||
|
r#"invalid value: string "h", expected "t" or "c""#
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens_error::<AdjacentlyTagged>(
|
||||||
|
&[
|
||||||
|
Token::Struct { name: "AdjacentlyTagged", len: 3},
|
||||||
|
|
||||||
|
Token::Str("c"),
|
||||||
|
Token::Unit,
|
||||||
|
|
||||||
|
Token::Str("h"),
|
||||||
|
],
|
||||||
|
r#"invalid value: string "h", expected "t" or "c""#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_enum_in_internally_tagged_enum() {
|
fn test_enum_in_internally_tagged_enum() {
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
|||||||
Reference in New Issue
Block a user