Compare commits

...

14 Commits

Author SHA1 Message Date
David Tolnay b1b9702daf Release 1.0.83 2018-12-27 19:53:48 -05:00
David Tolnay 32728d2f1d Format with rustfmt 2018-12-10 2018-12-27 19:52:26 -05:00
David Tolnay 807a097387 Fix spelling in ui test name 2018-12-27 19:51:53 -05:00
David Tolnay 794ee15386 Merge pull request #1448 from motu42/master
Allow #[serde(tag="...")] on structs
2018-12-27 19:47:28 -05:00
Johannes Willbold 2359417804 Added ui tests, Limited serde(tag = "...") to structs with named field
Added ui test struct-representation/internally-tagged-unit
Added ui test struct-representation/internally-tagged-tuple
    
Limited the serde(tag = "...") to enums and structs with named field
2018-12-28 01:29:33 +01:00
David Tolnay 7950f3cdc5 Format with rustfmt 2018-12-10 2018-12-27 15:35:43 -05:00
David Tolnay b87f8f35ee Merge pull request 1447 from vincascm/master 2018-12-27 15:29:16 -05:00
Johannes Willbold 9e53405f43 Fix for rustc 1.15.0 2018-12-27 21:21:46 +01:00
David Tolnay c6c1d8fa86 Work around deprecation of str::trim_left_matches 2018-12-27 15:20:32 -05:00
Johannes Willbold 8aa5c2b45d Removed deprected ui/enum-representation/internally-tagged-struct test 2018-12-27 20:53:08 +01:00
Johannes Willbold 414fd694c0 Allowed serde(tag="...") on structs
Added test test_internally_tagged_struct
Renamed EnumTag to TagType as it now also used for structs 
Modified serialize_struct_as_struct
2018-12-27 20:18:36 +01:00
vinoca 7e82809592 Fix tests fail since modify Container attributes rename_all 2018-12-20 14:30:23 +08:00
vinoca 0dae5db30e Support Container attributes rename_all only for Serialize or Deserialize 2018-12-19 09:46:52 +08:00
David Tolnay 5c24f0f0f3 Clean up some indentation that isn't visible to rustfmt 2018-12-10 22:31:39 -08:00
20 changed files with 329 additions and 136 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde" 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>"] 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"
+18 -10
View File
@@ -237,12 +237,16 @@ macro_rules! declare_error_trait {
#[cold] #[cold]
fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self { fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self {
if expected.is_empty() { if expected.is_empty() {
Error::custom(format_args!("unknown variant `{}`, there are no variants", Error::custom(format_args!(
variant)) "unknown variant `{}`, there are no variants",
variant
))
} else { } else {
Error::custom(format_args!("unknown variant `{}`, expected {}", Error::custom(format_args!(
variant, "unknown variant `{}`, expected {}",
OneOf { names: expected })) variant,
OneOf { names: expected }
))
} }
} }
@@ -251,12 +255,16 @@ macro_rules! declare_error_trait {
#[cold] #[cold]
fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
if expected.is_empty() { if expected.is_empty() {
Error::custom(format_args!("unknown field `{}`, there are no fields", Error::custom(format_args!(
field)) "unknown field `{}`, there are no fields",
field
))
} else { } else {
Error::custom(format_args!("unknown field `{}`, expected {}", Error::custom(format_args!(
field, "unknown field `{}`, expected {}",
OneOf { names: expected })) field,
OneOf { names: expected }
))
} }
} }
+1 -1
View File
@@ -75,7 +75,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.82")] #![doc(html_root_url = "https://docs.rs/serde/1.0.83")]
// 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
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_derive" 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>"] 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)]"
+32 -38
View File
@@ -5,11 +5,11 @@ use syn::spanned::Spanned;
use syn::{self, Ident, Index, Member}; use syn::{self, Ident, Index, Member};
use bound; use bound;
use dummy;
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::{attr, Ctxt, Derive}; use internals::{attr, Ctxt, Derive};
use pretend; use pretend;
use try;
use std::collections::BTreeSet; use std::collections::BTreeSet;
@@ -25,11 +25,6 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream
let ident = &cont.ident; let ident = &cont.ident;
let params = Parameters::new(&cont); let params = Parameters::new(&cont);
let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&params); let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&params);
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, &params)); let body = Stmts(deserialize_body(&cont, &params));
let delife = params.borrowed.de_lifetime(); 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(); Ok(dummy::wrap_in_const("DESERIALIZE", ident, impl_block))
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)
} }
fn precondition(cx: &Ctxt, cont: &Container) { fn precondition(cx: &Ctxt, cont: &Container) {
@@ -1145,15 +1128,15 @@ fn deserialize_enum(
cattrs: &attr::Container, cattrs: &attr::Container,
) -> Fragment { ) -> Fragment {
match *cattrs.tag() { match *cattrs.tag() {
attr::EnumTag::External => deserialize_externally_tagged_enum(params, variants, cattrs), attr::TagType::External => deserialize_externally_tagged_enum(params, variants, cattrs),
attr::EnumTag::Internal { ref tag } => { attr::TagType::Internal { ref tag } => {
deserialize_internally_tagged_enum(params, variants, cattrs, tag) deserialize_internally_tagged_enum(params, variants, cattrs, tag)
} }
attr::EnumTag::Adjacent { attr::TagType::Adjacent {
ref tag, ref tag,
ref content, ref content,
} => deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, 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 #variants_stmt
_serde::Deserializer::deserialize_enum(__deserializer, #type_name, VARIANTS, _serde::Deserializer::deserialize_enum(
__Visitor { __deserializer,
marker: _serde::export::PhantomData::<#this #ty_generics>, #type_name,
lifetime: _serde::export::PhantomData, 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)) { match try!(_serde::de::SeqAccess::next_element(&mut __seq)) {
_serde::export::Some(__field) => { _serde::export::Some(__field) => {
// Visit the second element - the content. // Visit the second element - the content.
match try!(_serde::de::SeqAccess::next_element_seed(&mut __seq, match try!(_serde::de::SeqAccess::next_element_seed(
__Seed { &mut __seq,
field: __field, __Seed {
marker: _serde::export::PhantomData, field: __field,
lifetime: _serde::export::PhantomData, marker: _serde::export::PhantomData,
})) { lifetime: _serde::export::PhantomData,
},
)) {
_serde::export::Some(__ret) => _serde::export::Ok(__ret), _serde::export::Some(__ret) => _serde::export::Ok(__ret),
// There is no second element. // There is no second element.
_serde::export::None => { _serde::export::None => {
@@ -1637,11 +1626,15 @@ fn deserialize_adjacently_tagged_enum(
} }
const FIELDS: &'static [&'static str] = &[#tag, #content]; const FIELDS: &'static [&'static str] = &[#tag, #content];
_serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, _serde::Deserializer::deserialize_struct(
__deserializer,
#type_name,
FIELDS,
__Visitor { __Visitor {
marker: _serde::export::PhantomData::<#this #ty_generics>, marker: _serde::export::PhantomData::<#this #ty_generics>,
lifetime: _serde::export::PhantomData, lifetime: _serde::export::PhantomData,
}) },
)
} }
} }
@@ -2247,8 +2240,9 @@ fn deserialize_identifier(
#variant_indices => _serde::export::Ok(#constructors), #variant_indices => _serde::export::Ok(#constructors),
)* )*
_ => _serde::export::Err(_serde::de::Error::invalid_value( _ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value), _serde::de::Unexpected::Unsigned(__value),
&#fallthrough_msg)) &#fallthrough_msg,
))
} }
} }
} }
+32
View File
@@ -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()
}
+5 -3
View File
@@ -85,12 +85,14 @@ impl<'a> Container<'a> {
match data { match data {
Data::Enum(ref mut variants) => { Data::Enum(ref mut variants) => {
for variant in 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 { for field in &mut variant.fields {
if field.attrs.flatten() { if field.attrs.flatten() {
has_flatten = true; 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() { if field.attrs.flatten() {
has_flatten = true; has_flatten = true;
} }
field.attrs.rename_by_rule(attrs.rename_all()); field.attrs.rename_by_rules(attrs.rename_all_rules());
} }
} }
} }
+140 -38
View File
@@ -95,7 +95,11 @@ pub struct Name {
deserialize: String, deserialize: String,
} }
#[allow(deprecated)]
fn unraw(ident: &Ident) -> String { 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() 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. /// Represents struct or enum attribute information.
pub struct Container { pub struct Container {
name: Name, name: Name,
transparent: bool, transparent: bool,
deny_unknown_fields: bool, deny_unknown_fields: bool,
default: Default, default: Default,
rename_all: RenameRule, rename_all_rules: RenameAllRules,
ser_bound: Option<Vec<syn::WherePredicate>>, ser_bound: Option<Vec<syn::WherePredicate>>,
de_bound: Option<Vec<syn::WherePredicate>>, de_bound: Option<Vec<syn::WherePredicate>>,
tag: EnumTag, tag: TagType,
type_from: Option<syn::Type>, type_from: Option<syn::Type>,
type_into: Option<syn::Type>, type_into: Option<syn::Type>,
remote: Option<syn::Path>, remote: Option<syn::Path>,
@@ -129,7 +138,7 @@ pub struct Container {
} }
/// Styles of representing an enum. /// Styles of representing an enum.
pub enum EnumTag { pub enum TagType {
/// The default. /// The default.
/// ///
/// ```json /// ```json
@@ -194,7 +203,8 @@ impl Container {
let mut transparent = BoolAttr::none(cx, "transparent"); let mut transparent = BoolAttr::none(cx, "transparent");
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields"); let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
let mut default = Attr::none(cx, "default"); 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 ser_bound = Attr::none(cx, "bound");
let mut de_bound = Attr::none(cx, "bound"); let mut de_bound = Attr::none(cx, "bound");
let mut untagged = BoolAttr::none(cx, "untagged"); let mut untagged = BoolAttr::none(cx, "untagged");
@@ -229,7 +239,10 @@ impl Container {
Meta(NameValue(ref m)) if m.ident == "rename_all" => { Meta(NameValue(ref m)) if m.ident == "rename_all" => {
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) { if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
match RenameRule::from_str(&s.value()) { 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( Err(()) => cx.error_spanned_by(
s, s,
format!( 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)]` // Parse `#[serde(transparent)]`
Meta(Word(ref word)) if word == "transparent" => { Meta(Word(ref word)) if word == "transparent" => {
transparent.set_true(word); transparent.set_true(word);
@@ -361,20 +410,27 @@ impl Container {
syn::Data::Enum(_) => { syn::Data::Enum(_) => {
internal_tag.set(&m.ident, s.value()); internal_tag.set(&m.ident, s.value());
} }
syn::Data::Struct(syn::DataStruct { syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
ref struct_token, .. match *fields {
}) => { syn::Fields::Named(_) => {
cx.error_spanned_by( internal_tag.set(&m.ident, s.value());
struct_token, }
"#[serde(tag = \"...\")] can only be used on enums", 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 { syn::Data::Union(syn::DataUnion {
ref union_token, .. ref union_token, ..
}) => { }) => {
cx.error_spanned_by( cx.error_spanned_by(
union_token, 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(), transparent: transparent.get(),
deny_unknown_fields: deny_unknown_fields.get(), deny_unknown_fields: deny_unknown_fields.get(),
default: default.get().unwrap_or(Default::None), 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(), ser_bound: ser_bound.get(),
de_bound: de_bound.get(), de_bound: de_bound.get(),
tag: decide_tag(cx, item, untagged, internal_tag, content), tag: decide_tag(cx, item, untagged, internal_tag, content),
@@ -481,8 +540,8 @@ impl Container {
&self.name &self.name
} }
pub fn rename_all(&self) -> &RenameRule { pub fn rename_all_rules(&self) -> &RenameAllRules {
&self.rename_all &self.rename_all_rules
} }
pub fn transparent(&self) -> bool { pub fn transparent(&self) -> bool {
@@ -505,7 +564,7 @@ impl Container {
self.de_bound.as_ref().map(|vec| &vec[..]) self.de_bound.as_ref().map(|vec| &vec[..])
} }
pub fn tag(&self) -> &EnumTag { pub fn tag(&self) -> &TagType {
&self.tag &self.tag
} }
@@ -540,14 +599,14 @@ fn decide_tag(
untagged: BoolAttr, untagged: BoolAttr,
internal_tag: Attr<String>, internal_tag: Attr<String>,
content: Attr<String>, content: Attr<String>,
) -> EnumTag { ) -> TagType {
match ( match (
untagged.0.get_with_tokens(), untagged.0.get_with_tokens(),
internal_tag.get_with_tokens(), internal_tag.get_with_tokens(),
content.get_with_tokens(), content.get_with_tokens(),
) { ) {
(None, None, None) => EnumTag::External, (None, None, None) => TagType::External,
(Some(_), None, None) => EnumTag::None, (Some(_), None, None) => TagType::None,
(None, Some((_, tag)), None) => { (None, Some((_, tag)), None) => {
// Check that there are no tuple variants. // Check that there are no tuple variants.
if let syn::Data::Enum(ref data) = item.data { 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) => { (Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
cx.error_spanned_by( cx.error_spanned_by(
@@ -578,14 +637,14 @@ fn decide_tag(
tag_tokens, tag_tokens,
"enum cannot be both untagged and internally tagged", "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, _))) => { (None, None, Some((content_tokens, _))) => {
cx.error_spanned_by( cx.error_spanned_by(
content_tokens, content_tokens,
"#[serde(tag = \"...\", content = \"...\")] must be used together", "#[serde(tag = \"...\", content = \"...\")] must be used together",
); );
EnumTag::External TagType::External
} }
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => { (Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
cx.error_spanned_by( cx.error_spanned_by(
@@ -596,9 +655,9 @@ fn decide_tag(
content_tokens, content_tokens,
"untagged enum cannot have #[serde(content = \"...\")]", "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, tag: tag,
content: content, content: content,
}, },
@@ -615,7 +674,7 @@ fn decide_tag(
content_tokens, content_tokens,
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]", "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
); );
EnumTag::External TagType::External
} }
} }
} }
@@ -705,7 +764,7 @@ pub struct Variant {
name: Name, name: Name,
ser_renamed: bool, ser_renamed: bool,
de_renamed: bool, de_renamed: bool,
rename_all: RenameRule, rename_all_rules: RenameAllRules,
ser_bound: Option<Vec<syn::WherePredicate>>, ser_bound: Option<Vec<syn::WherePredicate>>,
de_bound: Option<Vec<syn::WherePredicate>>, de_bound: Option<Vec<syn::WherePredicate>>,
skip_deserializing: bool, skip_deserializing: bool,
@@ -722,7 +781,8 @@ impl Variant {
let mut de_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename");
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_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 ser_bound = Attr::none(cx, "bound");
let mut de_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");
@@ -753,7 +813,10 @@ impl Variant {
Meta(NameValue(ref m)) if m.ident == "rename_all" => { Meta(NameValue(ref m)) if m.ident == "rename_all" => {
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) { if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
match RenameRule::from_str(&s.value()) { 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( Err(()) => cx.error_spanned_by(
s, s,
format!( 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)]` // Parse `#[serde(skip)]`
Meta(Word(ref word)) if word == "skip" => { Meta(Word(ref word)) if word == "skip" => {
skip_serializing.set_true(word); skip_serializing.set_true(word);
@@ -875,7 +974,10 @@ 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_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(), ser_bound: ser_bound.get(),
de_bound: de_bound.get(), de_bound: de_bound.get(),
skip_deserializing: skip_deserializing.get(), skip_deserializing: skip_deserializing.get(),
@@ -891,17 +993,17 @@ impl Variant {
&self.name &self.name
} }
pub fn rename_by_rule(&mut self, rule: &RenameRule) { pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
if !self.ser_renamed { 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 { 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 { pub fn rename_all_rules(&self) -> &RenameAllRules {
&self.rename_all &self.rename_all_rules
} }
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> { pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
@@ -1260,12 +1362,12 @@ impl Field {
&self.name &self.name
} }
pub fn rename_by_rule(&mut self, rule: &RenameRule) { pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
if !self.ser_renamed { 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 { 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);
} }
} }
+1 -1
View File
@@ -10,7 +10,7 @@ use std::str::FromStr;
use self::RenameRule::*; use self::RenameRule::*;
/// The different possible ways to change case of fields in a struct, or variants in an enum. /// 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 { pub enum RenameRule {
/// Don't apply a default rename rule. /// Don't apply a default rename rule.
None, None,
+6 -6
View File
@@ -1,5 +1,5 @@
use internals::ast::{Container, Data, Field, Style}; use internals::ast::{Container, Data, Field, Style};
use internals::attr::{EnumTag, Identifier}; use internals::attr::{Identifier, TagType};
use internals::{Ctxt, Derive}; use internals::{Ctxt, Derive};
use syn::{Member, Type}; use syn::{Member, Type};
@@ -127,7 +127,7 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
} }
// Variant with `other` attribute cannot appear in untagged enum // Variant with `other` attribute cannot appear in untagged enum
(_, Identifier::No, true, &EnumTag::None) => { (_, Identifier::No, true, &TagType::None) => {
cx.error_spanned_by( cx.error_spanned_by(
variant.original, variant.original,
"#[serde(other)] cannot appear on untagged enum", "#[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() { let tag = match *cont.attrs.tag() {
EnumTag::Internal { ref tag } => tag.as_str(), TagType::Internal { ref tag } => tag.as_str(),
EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return, TagType::External | TagType::Adjacent { .. } | TagType::None => return,
}; };
let diagnose_conflict = || { 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. /// contents tag must differ, for the same reason.
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) { fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
let (type_tag, content_tag) = match *cont.attrs.tag() { let (type_tag, content_tag) = match *cont.attrs.tag() {
EnumTag::Adjacent { TagType::Adjacent {
ref tag, ref tag,
ref content, ref content,
} => (tag, content), } => (tag, content),
EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return, TagType::Internal { .. } | TagType::External | TagType::None => return,
}; };
if type_tag == content_tag { if type_tag == content_tag {
+2 -1
View File
@@ -14,7 +14,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.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", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))] #![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy lints // Whitelisted clippy lints
@@ -69,6 +69,7 @@ mod bound;
mod fragment; mod fragment;
mod de; mod de;
mod dummy;
mod pretend; mod pretend;
mod ser; mod ser;
mod try; mod try;
+26 -25
View File
@@ -3,11 +3,11 @@ use syn::spanned::Spanned;
use syn::{self, Ident, Index, Member}; use syn::{self, Ident, Index, Member};
use bound; use bound;
use dummy;
use fragment::{Fragment, Match, Stmts}; use fragment::{Fragment, Match, Stmts};
use internals::ast::{Container, Data, Field, Style, Variant}; use internals::ast::{Container, Data, Field, Style, Variant};
use internals::{attr, Ctxt, Derive}; use internals::{attr, Ctxt, Derive};
use pretend; use pretend;
use try;
pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> { pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
let ctxt = Ctxt::new(); let ctxt = Ctxt::new();
@@ -21,11 +21,6 @@ pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream,
let ident = &cont.ident; let ident = &cont.ident;
let params = Parameters::new(&cont); let params = Parameters::new(&cont);
let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl(); 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, &params)); let body = Stmts(serialize_body(&cont, &params));
let impl_block = if let Some(remote) = cont.attrs.remote() { 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(); Ok(dummy::wrap_in_const("SERIALIZE", ident, impl_block))
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)
} }
fn precondition(cx: &Ctxt, cont: &Container) { fn precondition(cx: &Ctxt, cont: &Container) {
@@ -311,11 +294,26 @@ fn serialize_struct_as_struct(
fields: &[Field], fields: &[Field],
cattrs: &attr::Container, cattrs: &attr::Container,
) -> Fragment { ) -> Fragment {
let serialize_fields = let mut serialize_fields =
serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct); serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct);
let type_name = cattrs.name().serialize_name(); 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 let mut serialized_fields = fields
.iter() .iter()
.filter(|&field| !field.attrs.skip_serializing()) .filter(|&field| !field.attrs.skip_serializing())
@@ -331,7 +329,10 @@ fn serialize_struct_as_struct(
quote!(if #path(#field_expr) { 0 } else { 1 }) 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! { quote_block! {
let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len)); 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() { let body = Match(match *cattrs.tag() {
attr::EnumTag::External => { attr::TagType::External => {
serialize_externally_tagged_variant(params, variant, variant_index, cattrs) 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) serialize_internally_tagged_variant(params, variant, cattrs, tag)
} }
attr::EnumTag::Adjacent { attr::TagType::Adjacent {
ref tag, ref tag,
ref content, ref content,
} => serialize_adjacently_tagged_variant(params, variant, cattrs, tag, 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! { quote! {
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_test" 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>"] 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"
+1 -1
View File
@@ -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", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))] #![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy lints // Whitelisted clippy lints
+37
View File
@@ -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] #[test]
fn test_enum_in_untagged_enum() { fn test_enum_in_untagged_enum() {
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[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
@@ -3,6 +3,6 @@ extern crate serde_derive;
#[derive(Serialize)] #[derive(Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
struct S; struct U;
fn main() {} 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