mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-22 21:48:02 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1b9702daf | |||
| 32728d2f1d | |||
| 807a097387 | |||
| 794ee15386 | |||
| 2359417804 | |||
| 7950f3cdc5 | |||
| b87f8f35ee | |||
| 9e53405f43 | |||
| c6c1d8fa86 | |||
| 8aa5c2b45d | |||
| 414fd694c0 | |||
| 7e82809592 | |||
| 0dae5db30e | |||
| 5c24f0f0f3 |
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "serde"
|
||||
version = "1.0.82" # remember to update html_root_url
|
||||
version = "1.0.83" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "A generic serialization/deserialization framework"
|
||||
|
||||
+18
-10
@@ -237,12 +237,16 @@ macro_rules! declare_error_trait {
|
||||
#[cold]
|
||||
fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self {
|
||||
if expected.is_empty() {
|
||||
Error::custom(format_args!("unknown variant `{}`, there are no variants",
|
||||
variant))
|
||||
Error::custom(format_args!(
|
||||
"unknown variant `{}`, there are no variants",
|
||||
variant
|
||||
))
|
||||
} else {
|
||||
Error::custom(format_args!("unknown variant `{}`, expected {}",
|
||||
variant,
|
||||
OneOf { names: expected }))
|
||||
Error::custom(format_args!(
|
||||
"unknown variant `{}`, expected {}",
|
||||
variant,
|
||||
OneOf { names: expected }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,12 +255,16 @@ macro_rules! declare_error_trait {
|
||||
#[cold]
|
||||
fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
|
||||
if expected.is_empty() {
|
||||
Error::custom(format_args!("unknown field `{}`, there are no fields",
|
||||
field))
|
||||
Error::custom(format_args!(
|
||||
"unknown field `{}`, there are no fields",
|
||||
field
|
||||
))
|
||||
} else {
|
||||
Error::custom(format_args!("unknown field `{}`, expected {}",
|
||||
field,
|
||||
OneOf { names: expected }))
|
||||
Error::custom(format_args!(
|
||||
"unknown field `{}`, expected {}",
|
||||
field,
|
||||
OneOf { names: expected }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Serde types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.82")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.83")]
|
||||
// Support using Serde without the standard library!
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
// Unstable functionality only if the user asks for it. For tracking and
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "serde_derive"
|
||||
version = "1.0.82" # remember to update html_root_url
|
||||
version = "1.0.83" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
|
||||
|
||||
+32
-38
@@ -5,11 +5,11 @@ use syn::spanned::Spanned;
|
||||
use syn::{self, Ident, Index, Member};
|
||||
|
||||
use bound;
|
||||
use dummy;
|
||||
use fragment::{Expr, Fragment, Match, Stmts};
|
||||
use internals::ast::{Container, Data, Field, Style, Variant};
|
||||
use internals::{attr, Ctxt, Derive};
|
||||
use pretend;
|
||||
use try;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
@@ -25,11 +25,6 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream
|
||||
let ident = &cont.ident;
|
||||
let params = Parameters::new(&cont);
|
||||
let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(¶ms);
|
||||
let suffix = ident.to_string().trim_left_matches("r#").to_owned();
|
||||
let dummy_const = Ident::new(
|
||||
&format!("_IMPL_DESERIALIZE_FOR_{}", suffix),
|
||||
Span::call_site(),
|
||||
);
|
||||
let body = Stmts(deserialize_body(&cont, ¶ms));
|
||||
let delife = params.borrowed.de_lifetime();
|
||||
|
||||
@@ -65,19 +60,7 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream
|
||||
}
|
||||
};
|
||||
|
||||
let try_replacement = try::replacement();
|
||||
let generated = quote! {
|
||||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
||||
const #dummy_const: () = {
|
||||
#[allow(unknown_lints)]
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
|
||||
#[allow(rust_2018_idioms)]
|
||||
extern crate serde as _serde;
|
||||
#try_replacement
|
||||
#impl_block
|
||||
};
|
||||
};
|
||||
Ok(generated)
|
||||
Ok(dummy::wrap_in_const("DESERIALIZE", ident, impl_block))
|
||||
}
|
||||
|
||||
fn precondition(cx: &Ctxt, cont: &Container) {
|
||||
@@ -1145,15 +1128,15 @@ fn deserialize_enum(
|
||||
cattrs: &attr::Container,
|
||||
) -> Fragment {
|
||||
match *cattrs.tag() {
|
||||
attr::EnumTag::External => deserialize_externally_tagged_enum(params, variants, cattrs),
|
||||
attr::EnumTag::Internal { ref tag } => {
|
||||
attr::TagType::External => deserialize_externally_tagged_enum(params, variants, cattrs),
|
||||
attr::TagType::Internal { ref tag } => {
|
||||
deserialize_internally_tagged_enum(params, variants, cattrs, tag)
|
||||
}
|
||||
attr::EnumTag::Adjacent {
|
||||
attr::TagType::Adjacent {
|
||||
ref tag,
|
||||
ref content,
|
||||
} => deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, content),
|
||||
attr::EnumTag::None => deserialize_untagged_enum(params, variants, cattrs),
|
||||
attr::TagType::None => deserialize_untagged_enum(params, variants, cattrs),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1260,11 +1243,15 @@ fn deserialize_externally_tagged_enum(
|
||||
|
||||
#variants_stmt
|
||||
|
||||
_serde::Deserializer::deserialize_enum(__deserializer, #type_name, VARIANTS,
|
||||
__Visitor {
|
||||
marker: _serde::export::PhantomData::<#this #ty_generics>,
|
||||
lifetime: _serde::export::PhantomData,
|
||||
})
|
||||
_serde::Deserializer::deserialize_enum(
|
||||
__deserializer,
|
||||
#type_name,
|
||||
VARIANTS,
|
||||
__Visitor {
|
||||
marker: _serde::export::PhantomData::<#this #ty_generics>,
|
||||
lifetime: _serde::export::PhantomData,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1615,12 +1602,14 @@ fn deserialize_adjacently_tagged_enum(
|
||||
match try!(_serde::de::SeqAccess::next_element(&mut __seq)) {
|
||||
_serde::export::Some(__field) => {
|
||||
// Visit the second element - the content.
|
||||
match try!(_serde::de::SeqAccess::next_element_seed(&mut __seq,
|
||||
__Seed {
|
||||
field: __field,
|
||||
marker: _serde::export::PhantomData,
|
||||
lifetime: _serde::export::PhantomData,
|
||||
})) {
|
||||
match try!(_serde::de::SeqAccess::next_element_seed(
|
||||
&mut __seq,
|
||||
__Seed {
|
||||
field: __field,
|
||||
marker: _serde::export::PhantomData,
|
||||
lifetime: _serde::export::PhantomData,
|
||||
},
|
||||
)) {
|
||||
_serde::export::Some(__ret) => _serde::export::Ok(__ret),
|
||||
// There is no second element.
|
||||
_serde::export::None => {
|
||||
@@ -1637,11 +1626,15 @@ fn deserialize_adjacently_tagged_enum(
|
||||
}
|
||||
|
||||
const FIELDS: &'static [&'static str] = &[#tag, #content];
|
||||
_serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS,
|
||||
_serde::Deserializer::deserialize_struct(
|
||||
__deserializer,
|
||||
#type_name,
|
||||
FIELDS,
|
||||
__Visitor {
|
||||
marker: _serde::export::PhantomData::<#this #ty_generics>,
|
||||
lifetime: _serde::export::PhantomData,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2247,8 +2240,9 @@ fn deserialize_identifier(
|
||||
#variant_indices => _serde::export::Ok(#constructors),
|
||||
)*
|
||||
_ => _serde::export::Err(_serde::de::Error::invalid_value(
|
||||
_serde::de::Unexpected::Unsigned(__value),
|
||||
&#fallthrough_msg))
|
||||
_serde::de::Unexpected::Unsigned(__value),
|
||||
&#fallthrough_msg,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
|
||||
use try;
|
||||
|
||||
pub fn wrap_in_const(trait_: &str, ty: &Ident, code: TokenStream) -> TokenStream {
|
||||
let try_replacement = try::replacement();
|
||||
|
||||
let dummy_const = Ident::new(
|
||||
&format!("_IMPL_{}_FOR_{}", trait_, unraw(ty)),
|
||||
Span::call_site(),
|
||||
);
|
||||
|
||||
quote! {
|
||||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
||||
const #dummy_const: () = {
|
||||
#[allow(unknown_lints)]
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
|
||||
#[allow(rust_2018_idioms)]
|
||||
extern crate serde as _serde;
|
||||
#try_replacement
|
||||
#code
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn unraw(ident: &Ident) -> String {
|
||||
// str::trim_start_matches was added in 1.30, trim_left_matches deprecated
|
||||
// in 1.33. We currently support rustc back to 1.15 so we need to continue
|
||||
// to use the deprecated one.
|
||||
ident.to_string().trim_left_matches("r#").to_owned()
|
||||
}
|
||||
@@ -85,12 +85,14 @@ impl<'a> Container<'a> {
|
||||
match data {
|
||||
Data::Enum(ref mut variants) => {
|
||||
for variant in variants {
|
||||
variant.attrs.rename_by_rule(attrs.rename_all());
|
||||
variant.attrs.rename_by_rules(attrs.rename_all_rules());
|
||||
for field in &mut variant.fields {
|
||||
if field.attrs.flatten() {
|
||||
has_flatten = true;
|
||||
}
|
||||
field.attrs.rename_by_rule(variant.attrs.rename_all());
|
||||
field
|
||||
.attrs
|
||||
.rename_by_rules(variant.attrs.rename_all_rules());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +101,7 @@ impl<'a> Container<'a> {
|
||||
if field.attrs.flatten() {
|
||||
has_flatten = true;
|
||||
}
|
||||
field.attrs.rename_by_rule(attrs.rename_all());
|
||||
field.attrs.rename_by_rules(attrs.rename_all_rules());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,11 @@ pub struct Name {
|
||||
deserialize: String,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn unraw(ident: &Ident) -> String {
|
||||
// str::trim_start_matches was added in 1.30, trim_left_matches deprecated
|
||||
// in 1.33. We currently support rustc back to 1.15 so we need to continue
|
||||
// to use the deprecated one.
|
||||
ident.to_string().trim_left_matches("r#").to_owned()
|
||||
}
|
||||
|
||||
@@ -111,16 +115,21 @@ impl Name {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RenameAllRules {
|
||||
serialize: RenameRule,
|
||||
deserialize: RenameRule,
|
||||
}
|
||||
|
||||
/// Represents struct or enum attribute information.
|
||||
pub struct Container {
|
||||
name: Name,
|
||||
transparent: bool,
|
||||
deny_unknown_fields: bool,
|
||||
default: Default,
|
||||
rename_all: RenameRule,
|
||||
rename_all_rules: RenameAllRules,
|
||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||
tag: EnumTag,
|
||||
tag: TagType,
|
||||
type_from: Option<syn::Type>,
|
||||
type_into: Option<syn::Type>,
|
||||
remote: Option<syn::Path>,
|
||||
@@ -129,7 +138,7 @@ pub struct Container {
|
||||
}
|
||||
|
||||
/// Styles of representing an enum.
|
||||
pub enum EnumTag {
|
||||
pub enum TagType {
|
||||
/// The default.
|
||||
///
|
||||
/// ```json
|
||||
@@ -194,7 +203,8 @@ impl Container {
|
||||
let mut transparent = BoolAttr::none(cx, "transparent");
|
||||
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
|
||||
let mut default = Attr::none(cx, "default");
|
||||
let mut rename_all = Attr::none(cx, "rename_all");
|
||||
let mut rename_all_ser_rule = Attr::none(cx, "rename_all");
|
||||
let mut rename_all_de_rule = Attr::none(cx, "rename_all");
|
||||
let mut ser_bound = Attr::none(cx, "bound");
|
||||
let mut de_bound = Attr::none(cx, "bound");
|
||||
let mut untagged = BoolAttr::none(cx, "untagged");
|
||||
@@ -229,7 +239,10 @@ impl Container {
|
||||
Meta(NameValue(ref m)) if m.ident == "rename_all" => {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
match RenameRule::from_str(&s.value()) {
|
||||
Ok(rename_rule) => rename_all.set(&m.ident, rename_rule),
|
||||
Ok(rename_rule) => {
|
||||
rename_all_ser_rule.set(&m.ident, rename_rule);
|
||||
rename_all_de_rule.set(&m.ident, rename_rule);
|
||||
}
|
||||
Err(()) => cx.error_spanned_by(
|
||||
s,
|
||||
format!(
|
||||
@@ -242,6 +255,42 @@ impl Container {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(rename_all(serialize = "foo", deserialize = "bar"))]`
|
||||
Meta(List(ref m)) if m.ident == "rename_all" => {
|
||||
if let Ok((ser, de)) = get_renames(cx, &m.nested) {
|
||||
if let Some(ser) = ser {
|
||||
match RenameRule::from_str(&ser.value()) {
|
||||
Ok(rename_rule) => {
|
||||
rename_all_ser_rule.set(&m.ident, rename_rule)
|
||||
}
|
||||
Err(()) => cx.error_spanned_by(
|
||||
ser,
|
||||
format!(
|
||||
"unknown rename rule for #[serde(rename_all \
|
||||
= {:?})]",
|
||||
ser.value(),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
if let Some(de) = de {
|
||||
match RenameRule::from_str(&de.value()) {
|
||||
Ok(rename_rule) => {
|
||||
rename_all_de_rule.set(&m.ident, rename_rule)
|
||||
}
|
||||
Err(()) => cx.error_spanned_by(
|
||||
de,
|
||||
format!(
|
||||
"unknown rename rule for #[serde(rename_all \
|
||||
= {:?})]",
|
||||
de.value(),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(transparent)]`
|
||||
Meta(Word(ref word)) if word == "transparent" => {
|
||||
transparent.set_true(word);
|
||||
@@ -361,20 +410,27 @@ impl Container {
|
||||
syn::Data::Enum(_) => {
|
||||
internal_tag.set(&m.ident, s.value());
|
||||
}
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
ref struct_token, ..
|
||||
}) => {
|
||||
cx.error_spanned_by(
|
||||
struct_token,
|
||||
"#[serde(tag = \"...\")] can only be used on enums",
|
||||
);
|
||||
syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
|
||||
match *fields {
|
||||
syn::Fields::Named(_) => {
|
||||
internal_tag.set(&m.ident, s.value());
|
||||
}
|
||||
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
|
||||
cx.error_spanned_by(
|
||||
fields,
|
||||
"#[serde(tag = \"...\")] can only be used on enums \
|
||||
and structs with named fields",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Data::Union(syn::DataUnion {
|
||||
ref union_token, ..
|
||||
}) => {
|
||||
cx.error_spanned_by(
|
||||
union_token,
|
||||
"#[serde(tag = \"...\")] can only be used on enums",
|
||||
"#[serde(tag = \"...\")] can only be used on enums \
|
||||
and structs with named fields",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -465,7 +521,10 @@ impl Container {
|
||||
transparent: transparent.get(),
|
||||
deny_unknown_fields: deny_unknown_fields.get(),
|
||||
default: default.get().unwrap_or(Default::None),
|
||||
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
||||
rename_all_rules: RenameAllRules {
|
||||
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
|
||||
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
|
||||
},
|
||||
ser_bound: ser_bound.get(),
|
||||
de_bound: de_bound.get(),
|
||||
tag: decide_tag(cx, item, untagged, internal_tag, content),
|
||||
@@ -481,8 +540,8 @@ impl Container {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn rename_all(&self) -> &RenameRule {
|
||||
&self.rename_all
|
||||
pub fn rename_all_rules(&self) -> &RenameAllRules {
|
||||
&self.rename_all_rules
|
||||
}
|
||||
|
||||
pub fn transparent(&self) -> bool {
|
||||
@@ -505,7 +564,7 @@ impl Container {
|
||||
self.de_bound.as_ref().map(|vec| &vec[..])
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> &EnumTag {
|
||||
pub fn tag(&self) -> &TagType {
|
||||
&self.tag
|
||||
}
|
||||
|
||||
@@ -540,14 +599,14 @@ fn decide_tag(
|
||||
untagged: BoolAttr,
|
||||
internal_tag: Attr<String>,
|
||||
content: Attr<String>,
|
||||
) -> EnumTag {
|
||||
) -> TagType {
|
||||
match (
|
||||
untagged.0.get_with_tokens(),
|
||||
internal_tag.get_with_tokens(),
|
||||
content.get_with_tokens(),
|
||||
) {
|
||||
(None, None, None) => EnumTag::External,
|
||||
(Some(_), None, None) => EnumTag::None,
|
||||
(None, None, None) => TagType::External,
|
||||
(Some(_), None, None) => TagType::None,
|
||||
(None, Some((_, tag)), None) => {
|
||||
// Check that there are no tuple variants.
|
||||
if let syn::Data::Enum(ref data) = item.data {
|
||||
@@ -567,7 +626,7 @@ fn decide_tag(
|
||||
}
|
||||
}
|
||||
}
|
||||
EnumTag::Internal { tag: tag }
|
||||
TagType::Internal { tag: tag }
|
||||
}
|
||||
(Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
|
||||
cx.error_spanned_by(
|
||||
@@ -578,14 +637,14 @@ fn decide_tag(
|
||||
tag_tokens,
|
||||
"enum cannot be both untagged and internally tagged",
|
||||
);
|
||||
EnumTag::External // doesn't matter, will error
|
||||
TagType::External // doesn't matter, will error
|
||||
}
|
||||
(None, None, Some((content_tokens, _))) => {
|
||||
cx.error_spanned_by(
|
||||
content_tokens,
|
||||
"#[serde(tag = \"...\", content = \"...\")] must be used together",
|
||||
);
|
||||
EnumTag::External
|
||||
TagType::External
|
||||
}
|
||||
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
|
||||
cx.error_spanned_by(
|
||||
@@ -596,9 +655,9 @@ fn decide_tag(
|
||||
content_tokens,
|
||||
"untagged enum cannot have #[serde(content = \"...\")]",
|
||||
);
|
||||
EnumTag::External
|
||||
TagType::External
|
||||
}
|
||||
(None, Some((_, tag)), Some((_, content))) => EnumTag::Adjacent {
|
||||
(None, Some((_, tag)), Some((_, content))) => TagType::Adjacent {
|
||||
tag: tag,
|
||||
content: content,
|
||||
},
|
||||
@@ -615,7 +674,7 @@ fn decide_tag(
|
||||
content_tokens,
|
||||
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
|
||||
);
|
||||
EnumTag::External
|
||||
TagType::External
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -705,7 +764,7 @@ pub struct Variant {
|
||||
name: Name,
|
||||
ser_renamed: bool,
|
||||
de_renamed: bool,
|
||||
rename_all: RenameRule,
|
||||
rename_all_rules: RenameAllRules,
|
||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||
skip_deserializing: bool,
|
||||
@@ -722,7 +781,8 @@ impl Variant {
|
||||
let mut de_name = Attr::none(cx, "rename");
|
||||
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
|
||||
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
||||
let mut rename_all = Attr::none(cx, "rename_all");
|
||||
let mut rename_all_ser_rule = Attr::none(cx, "rename_all");
|
||||
let mut rename_all_de_rule = 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");
|
||||
@@ -753,7 +813,10 @@ impl Variant {
|
||||
Meta(NameValue(ref m)) if m.ident == "rename_all" => {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
match RenameRule::from_str(&s.value()) {
|
||||
Ok(rename_rule) => rename_all.set(&m.ident, rename_rule),
|
||||
Ok(rename_rule) => {
|
||||
rename_all_ser_rule.set(&m.ident, rename_rule);
|
||||
rename_all_de_rule.set(&m.ident, rename_rule);
|
||||
}
|
||||
Err(()) => cx.error_spanned_by(
|
||||
s,
|
||||
format!(
|
||||
@@ -766,6 +829,42 @@ impl Variant {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(rename_all(serialize = "foo", deserialize = "bar"))]`
|
||||
Meta(List(ref m)) if m.ident == "rename_all" => {
|
||||
if let Ok((ser, de)) = get_renames(cx, &m.nested) {
|
||||
if let Some(ser) = ser {
|
||||
match RenameRule::from_str(&ser.value()) {
|
||||
Ok(rename_rule) => {
|
||||
rename_all_ser_rule.set(&m.ident, rename_rule)
|
||||
}
|
||||
Err(()) => cx.error_spanned_by(
|
||||
ser,
|
||||
format!(
|
||||
"unknown rename rule for #[serde(rename_all \
|
||||
= {:?})]",
|
||||
ser.value(),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
if let Some(de) = de {
|
||||
match RenameRule::from_str(&de.value()) {
|
||||
Ok(rename_rule) => {
|
||||
rename_all_de_rule.set(&m.ident, rename_rule)
|
||||
}
|
||||
Err(()) => cx.error_spanned_by(
|
||||
de,
|
||||
format!(
|
||||
"unknown rename rule for #[serde(rename_all \
|
||||
= {:?})]",
|
||||
de.value(),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip)]`
|
||||
Meta(Word(ref word)) if word == "skip" => {
|
||||
skip_serializing.set_true(word);
|
||||
@@ -875,7 +974,10 @@ impl Variant {
|
||||
},
|
||||
ser_renamed: ser_renamed,
|
||||
de_renamed: de_renamed,
|
||||
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
||||
rename_all_rules: RenameAllRules {
|
||||
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
|
||||
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
|
||||
},
|
||||
ser_bound: ser_bound.get(),
|
||||
de_bound: de_bound.get(),
|
||||
skip_deserializing: skip_deserializing.get(),
|
||||
@@ -891,17 +993,17 @@ impl Variant {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn rename_by_rule(&mut self, rule: &RenameRule) {
|
||||
pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
|
||||
if !self.ser_renamed {
|
||||
self.name.serialize = rule.apply_to_variant(&self.name.serialize);
|
||||
self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize);
|
||||
}
|
||||
if !self.de_renamed {
|
||||
self.name.deserialize = rule.apply_to_variant(&self.name.deserialize);
|
||||
self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename_all(&self) -> &RenameRule {
|
||||
&self.rename_all
|
||||
pub fn rename_all_rules(&self) -> &RenameAllRules {
|
||||
&self.rename_all_rules
|
||||
}
|
||||
|
||||
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||
@@ -1260,12 +1362,12 @@ impl Field {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn rename_by_rule(&mut self, rule: &RenameRule) {
|
||||
pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
|
||||
if !self.ser_renamed {
|
||||
self.name.serialize = rule.apply_to_field(&self.name.serialize);
|
||||
self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize);
|
||||
}
|
||||
if !self.de_renamed {
|
||||
self.name.deserialize = rule.apply_to_field(&self.name.deserialize);
|
||||
self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::str::FromStr;
|
||||
use self::RenameRule::*;
|
||||
|
||||
/// The different possible ways to change case of fields in a struct, or variants in an enum.
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum RenameRule {
|
||||
/// Don't apply a default rename rule.
|
||||
None,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use internals::ast::{Container, Data, Field, Style};
|
||||
use internals::attr::{EnumTag, Identifier};
|
||||
use internals::attr::{Identifier, TagType};
|
||||
use internals::{Ctxt, Derive};
|
||||
use syn::{Member, Type};
|
||||
|
||||
@@ -127,7 +127,7 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
|
||||
}
|
||||
|
||||
// Variant with `other` attribute cannot appear in untagged enum
|
||||
(_, Identifier::No, true, &EnumTag::None) => {
|
||||
(_, Identifier::No, true, &TagType::None) => {
|
||||
cx.error_spanned_by(
|
||||
variant.original,
|
||||
"#[serde(other)] cannot appear on untagged enum",
|
||||
@@ -276,8 +276,8 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
|
||||
};
|
||||
|
||||
let tag = match *cont.attrs.tag() {
|
||||
EnumTag::Internal { ref tag } => tag.as_str(),
|
||||
EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return,
|
||||
TagType::Internal { ref tag } => tag.as_str(),
|
||||
TagType::External | TagType::Adjacent { .. } | TagType::None => return,
|
||||
};
|
||||
|
||||
let diagnose_conflict = || {
|
||||
@@ -312,11 +312,11 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
|
||||
/// contents tag must differ, for the same reason.
|
||||
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
|
||||
let (type_tag, content_tag) = match *cont.attrs.tag() {
|
||||
EnumTag::Adjacent {
|
||||
TagType::Adjacent {
|
||||
ref tag,
|
||||
ref content,
|
||||
} => (tag, content),
|
||||
EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return,
|
||||
TagType::Internal { .. } | TagType::External | TagType::None => return,
|
||||
};
|
||||
|
||||
if type_tag == content_tag {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//!
|
||||
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.82")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.83")]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Whitelisted clippy lints
|
||||
@@ -69,6 +69,7 @@ mod bound;
|
||||
mod fragment;
|
||||
|
||||
mod de;
|
||||
mod dummy;
|
||||
mod pretend;
|
||||
mod ser;
|
||||
mod try;
|
||||
|
||||
+26
-25
@@ -3,11 +3,11 @@ use syn::spanned::Spanned;
|
||||
use syn::{self, Ident, Index, Member};
|
||||
|
||||
use bound;
|
||||
use dummy;
|
||||
use fragment::{Fragment, Match, Stmts};
|
||||
use internals::ast::{Container, Data, Field, Style, Variant};
|
||||
use internals::{attr, Ctxt, Derive};
|
||||
use pretend;
|
||||
use try;
|
||||
|
||||
pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
|
||||
let ctxt = Ctxt::new();
|
||||
@@ -21,11 +21,6 @@ pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream,
|
||||
let ident = &cont.ident;
|
||||
let params = Parameters::new(&cont);
|
||||
let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
let suffix = ident.to_string().trim_left_matches("r#").to_owned();
|
||||
let dummy_const = Ident::new(
|
||||
&format!("_IMPL_SERIALIZE_FOR_{}", suffix),
|
||||
Span::call_site(),
|
||||
);
|
||||
let body = Stmts(serialize_body(&cont, ¶ms));
|
||||
|
||||
let impl_block = if let Some(remote) = cont.attrs.remote() {
|
||||
@@ -56,19 +51,7 @@ pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream,
|
||||
}
|
||||
};
|
||||
|
||||
let try_replacement = try::replacement();
|
||||
let generated = quote! {
|
||||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
||||
const #dummy_const: () = {
|
||||
#[allow(unknown_lints)]
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
|
||||
#[allow(rust_2018_idioms)]
|
||||
extern crate serde as _serde;
|
||||
#try_replacement
|
||||
#impl_block
|
||||
};
|
||||
};
|
||||
Ok(generated)
|
||||
Ok(dummy::wrap_in_const("SERIALIZE", ident, impl_block))
|
||||
}
|
||||
|
||||
fn precondition(cx: &Ctxt, cont: &Container) {
|
||||
@@ -311,11 +294,26 @@ fn serialize_struct_as_struct(
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
) -> Fragment {
|
||||
let serialize_fields =
|
||||
let mut serialize_fields =
|
||||
serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct);
|
||||
|
||||
let type_name = cattrs.name().serialize_name();
|
||||
|
||||
let additional_field_count: usize = match cattrs.tag() {
|
||||
&attr::TagType::Internal { ref tag } => {
|
||||
let func = StructTrait::SerializeStruct.serialize_field(Span::call_site());
|
||||
serialize_fields.insert(
|
||||
0,
|
||||
quote! {
|
||||
try!(#func(&mut __serde_state, #tag, #type_name));
|
||||
},
|
||||
);
|
||||
|
||||
1
|
||||
}
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let mut serialized_fields = fields
|
||||
.iter()
|
||||
.filter(|&field| !field.attrs.skip_serializing())
|
||||
@@ -331,7 +329,10 @@ fn serialize_struct_as_struct(
|
||||
quote!(if #path(#field_expr) { 0 } else { 1 })
|
||||
}
|
||||
})
|
||||
.fold(quote!(0), |sum, expr| quote!(#sum + #expr));
|
||||
.fold(
|
||||
quote!(#additional_field_count),
|
||||
|sum, expr| quote!(#sum + #expr),
|
||||
);
|
||||
|
||||
quote_block! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len));
|
||||
@@ -452,17 +453,17 @@ fn serialize_variant(
|
||||
};
|
||||
|
||||
let body = Match(match *cattrs.tag() {
|
||||
attr::EnumTag::External => {
|
||||
attr::TagType::External => {
|
||||
serialize_externally_tagged_variant(params, variant, variant_index, cattrs)
|
||||
}
|
||||
attr::EnumTag::Internal { ref tag } => {
|
||||
attr::TagType::Internal { ref tag } => {
|
||||
serialize_internally_tagged_variant(params, variant, cattrs, tag)
|
||||
}
|
||||
attr::EnumTag::Adjacent {
|
||||
attr::TagType::Adjacent {
|
||||
ref tag,
|
||||
ref content,
|
||||
} => serialize_adjacently_tagged_variant(params, variant, cattrs, tag, content),
|
||||
attr::EnumTag::None => serialize_untagged_variant(params, variant, cattrs),
|
||||
attr::TagType::None => serialize_untagged_variant(params, variant, cattrs),
|
||||
});
|
||||
|
||||
quote! {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "serde_test"
|
||||
version = "1.0.82" # remember to update html_root_url
|
||||
version = "1.0.83" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "Token De/Serializer for testing De/Serialize implementations"
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.82")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.83")]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Whitelisted clippy lints
|
||||
|
||||
@@ -1376,6 +1376,43 @@ fn test_enum_in_internally_tagged_enum() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internally_tagged_struct() {
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub struct Struct {
|
||||
a: u8,
|
||||
}
|
||||
|
||||
assert_tokens(
|
||||
&Struct { a: 1 },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "Struct",
|
||||
len: 2,
|
||||
},
|
||||
Token::Str("type"),
|
||||
Token::Str("Struct"),
|
||||
Token::Str("a"),
|
||||
Token::U8(1),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_de_tokens(
|
||||
&Struct { a: 1 },
|
||||
&[
|
||||
Token::Struct {
|
||||
name: "Struct",
|
||||
len: 1,
|
||||
},
|
||||
Token::Str("a"),
|
||||
Token::U8(1),
|
||||
Token::StructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum_in_untagged_enum() {
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
error: #[serde(tag = "...")] can only be used on enums
|
||||
--> $DIR/internally-tagged-struct.rs:6:1
|
||||
|
|
||||
6 | struct S;
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
struct S(u8, u8);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
error: #[serde(tag = "...")] can only be used on enums and structs with named fields
|
||||
--> $DIR/internally-tagged-tuple.rs:6:9
|
||||
|
|
||||
6 | struct S(u8, u8);
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
+1
-1
@@ -3,6 +3,6 @@ extern crate serde_derive;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
struct S;
|
||||
struct U;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
error: #[serde(tag = "...")] can only be used on enums and structs with named fields
|
||||
--> $DIR/internally-tagged-unit.rs:4:10
|
||||
|
|
||||
4 | #[derive(Serialize)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
Reference in New Issue
Block a user