Compare commits

...

13 Commits

Author SHA1 Message Date
David Tolnay bb99b31eb0 Release 1.0.85 2019-01-18 22:36:49 -08:00
David Tolnay 84397183f3 Fix spelling of alises -> aliases 2019-01-18 22:34:23 -08:00
David Tolnay aeae265777 Simpler way to get single element from vector 2019-01-18 22:33:43 -08:00
David Tolnay a9c5df5da1 Remove unused Clone on attr::Attr 2019-01-18 22:31:25 -08:00
David Tolnay 96576c4de9 Merge pull request #1458 from Lymia/master
Implements alias annotation and allow multiple deserialization renames.
2019-01-18 22:29:57 -08:00
David Tolnay 9ec68e5829 Re-export is no longer just for optional serde cfg 2019-01-18 00:48:05 -08:00
David Tolnay face857d5e Update crates.io readme to 2018 edition 2019-01-18 00:44:17 -08:00
David Tolnay 85a1cc9b4f Merge pull request #1460 from dtolnay/readme
Replace serde_derive with features = ["derive"] in readme
2019-01-18 00:43:25 -08:00
David Tolnay 630501b93d Replace serde_derive with features = ["derive"] in readme 2019-01-18 00:30:01 -08:00
Lymia Aluysia 8bbc2995ca Fix clippy lint in serde_derive 2019-01-15 11:35:26 -06:00
Lymia Aluysia 7d3872df57 Fix compilation on Rust 1.15.x 2019-01-15 11:29:55 -06:00
Lymia Aluysia 1ed228b92b Implements alias annotation and allow multiple deserialization renames. 2019-01-15 11:15:01 -06:00
David Tolnay b605cd1bb9 Make compiletest setup consistent with serde_json 2019-01-12 16:22:23 -08:00
17 changed files with 405 additions and 231 deletions
+6 -12
View File
@@ -25,19 +25,17 @@ You may be looking for:
<details> <details>
<summary> <summary>
Click to show Cargo.toml. Click to show Cargo.toml.
<a href="https://play.rust-lang.org/?gist=9003c5b88c1f4989941925d7190c6eec" target="_blank">Run this code in the playground.</a> <a href="https://play.rust-lang.org/?edition=2018&gist=72755f28f99afc95e01d63174b28c1f5" target="_blank">Run this code in the playground.</a>
</summary> </summary>
```toml ```toml
[dependencies] [dependencies]
# The core APIs, including the Serialize and Deserialize traits. Always # The core APIs, including the Serialize and Deserialize traits. Always
# required when using Serde. # required when using Serde. The "derive" feature is only required when
serde = "1.0" # using #[derive(Serialize, Deserialize)] to make Serde work with structs
# and enums defined in your crate.
# Support for #[derive(Serialize, Deserialize)]. Required if you want Serde serde = { version = "1.0", features = ["derive"] }
# to work for structs and enums defined in your crate.
serde_derive = "1.0"
# Each data format lives in its own crate; the sample code below uses JSON # Each data format lives in its own crate; the sample code below uses JSON
# but you may be using a different one. # but you may be using a different one.
@@ -48,11 +46,7 @@ serde_json = "1.0"
<p></p> <p></p>
```rust ```rust
#[macro_use] use serde::{Serialize, Deserialize};
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct Point { struct Point {
+1 -5
View File
@@ -16,11 +16,7 @@ You may be looking for:
## Serde in action ## Serde in action
```rust ```rust
#[macro_use] use serde::{Serialize, Deserialize};
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct Point { struct Point {
+2 -25
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde" name = "serde"
version = "1.0.84" # remember to update html_root_url version = "1.0.85" # 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"
@@ -32,30 +32,7 @@ features = ["derive", "rc"]
[features] [features]
default = ["std"] default = ["std"]
# Re-export the derive(Serialize, Deserialize) macros. This is intended for # Provide derive(Serialize, Deserialize) macros.
# library crates that provide optional Serde impls behind a Cargo cfg of their
# own.
#
# Mainly this is a workaround for limitations associated with
# rust-lang/cargo#1286 in which a library crate cannot use one "serde" cfg in
# Cargo to enable dependencies on both serde and serde_derive crates.
#
# The recommended way to provide optional Serde support that requires derive is
# as follows. In particular, please do not name your library's Serde feature
# anything other than "serde".
#
# [dependencies]
# serde = { version = "1.0", optional = true, features = ["derive"] }
#
# Within the library, these optional Serde derives would be written like this.
#
# #[cfg(feature = "serde")]
# #[macro_use]
# extern crate serde;
#
# #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
# struct ...
#
derive = ["serde_derive"] derive = ["serde_derive"]
# Provide impls for common standard library types like Vec<T> and HashMap<K, V>. # Provide impls for common standard library types like Vec<T> and HashMap<K, V>.
+1 -35
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.84")] #![doc(html_root_url = "https://docs.rs/serde/1.0.85")]
// 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
@@ -252,40 +252,6 @@ pub mod private;
// Re-export #[derive(Serialize, Deserialize)]. // Re-export #[derive(Serialize, Deserialize)].
// //
// This is a workaround for https://github.com/rust-lang/cargo/issues/1286.
// Without this re-export, crates that put Serde derives behind a cfg_attr would
// need to use some silly feature name that depends on both serde and
// serde_derive.
//
// [features]
// serde-impls = ["serde", "serde_derive"]
//
// [dependencies]
// serde = { version = "1.0", optional = true }
// serde_derive = { version = "1.0", optional = true }
//
// # Used like this:
// # #[cfg(feature = "serde-impls")]
// # #[macro_use]
// # extern crate serde_derive;
// #
// # #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
// # struct S { /* ... */ }
//
// The re-exported derives allow crates to use "serde" as the name of their
// Serde feature which is more intuitive.
//
// [dependencies]
// serde = { version = "1.0", optional = true, features = ["derive"] }
//
// # Used like this:
// # #[cfg(feature = "serde")]
// # #[macro_use]
// # extern crate serde;
// #
// # #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
// # struct S { /* ... */ }
//
// The reason re-exporting is not enabled by default is that disabling it would // The reason re-exporting is not enabled by default is that disabling it would
// be annoying for crates that provide handwritten impls or data formats. They // be annoying for crates that provide handwritten impls or data formats. They
// would need to disable default features and then explicitly re-enable std. // would need to disable default features and then explicitly re-enable std.
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_derive" name = "serde_derive"
version = "1.0.84" # remember to update html_root_url version = "1.0.85" # 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)]"
+57 -77
View File
@@ -1140,25 +1140,17 @@ fn deserialize_enum(
} }
} }
fn deserialize_externally_tagged_enum( fn prepare_enum_variant_enum(
params: &Parameters,
variants: &[Variant], variants: &[Variant],
cattrs: &attr::Container, cattrs: &attr::Container,
) -> Fragment { ) -> (TokenStream, Stmts) {
let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime();
let type_name = cattrs.name().deserialize_name();
let expecting = format!("enum {}", params.type_name());
let variant_names_idents: Vec<_> = variants let variant_names_idents: Vec<_> = variants
.iter() .iter()
.enumerate() .enumerate()
.filter(|&(_, variant)| !variant.attrs.skip_deserializing()) .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
.map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i))) .map(|(i, variant)| {
(variant.attrs.name().deserialize_name(), field_i(i), variant.attrs.aliases())
})
.collect(); .collect();
let other_idx = variants let other_idx = variants
@@ -1166,7 +1158,7 @@ fn deserialize_externally_tagged_enum(
.position(|ref variant| variant.attrs.other()); .position(|ref variant| variant.attrs.other());
let variants_stmt = { let variants_stmt = {
let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name); let variant_names = variant_names_idents.iter().map(|&(ref name, _, _)| name);
quote! { quote! {
const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ]; const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
} }
@@ -1179,6 +1171,24 @@ fn deserialize_externally_tagged_enum(
other_idx, other_idx,
)); ));
(variants_stmt, variant_visitor)
}
fn deserialize_externally_tagged_enum(
params: &Parameters,
variants: &[Variant],
cattrs: &attr::Container,
) -> Fragment {
let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime();
let type_name = cattrs.name().deserialize_name();
let expecting = format!("enum {}", params.type_name());
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
// Match arms to extract a variant from a string // Match arms to extract a variant from a string
let variant_arms = variants let variant_arms = variants
.iter() .iter()
@@ -1261,30 +1271,7 @@ fn deserialize_internally_tagged_enum(
cattrs: &attr::Container, cattrs: &attr::Container,
tag: &str, tag: &str,
) -> Fragment { ) -> Fragment {
let variant_names_idents: Vec<_> = variants let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
.iter()
.enumerate()
.filter(|&(_, variant)| !variant.attrs.skip_deserializing())
.map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i)))
.collect();
let other_idx = variants
.iter()
.position(|ref variant| variant.attrs.other());
let variants_stmt = {
let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name);
quote! {
const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
}
};
let variant_visitor = Stmts(deserialize_generated_identifier(
&variant_names_idents,
cattrs,
true,
other_idx,
));
// Match arms to extract a variant from a string // Match arms to extract a variant from a string
let variant_arms = variants let variant_arms = variants
@@ -1335,30 +1322,7 @@ fn deserialize_adjacently_tagged_enum(
split_with_de_lifetime(params); split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime(); let delife = params.borrowed.de_lifetime();
let variant_names_idents: Vec<_> = variants let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
.iter()
.enumerate()
.filter(|&(_, variant)| !variant.attrs.skip_deserializing())
.map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i)))
.collect();
let other_idx = variants
.iter()
.position(|ref variant| variant.attrs.other());
let variants_stmt = {
let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name);
quote! {
const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
}
};
let variant_visitor = Stmts(deserialize_generated_identifier(
&variant_names_idents,
cattrs,
true,
other_idx,
));
let variant_arms: &Vec<_> = &variants let variant_arms: &Vec<_> = &variants
.iter() .iter()
@@ -1870,13 +1834,13 @@ fn deserialize_untagged_newtype_variant(
} }
fn deserialize_generated_identifier( fn deserialize_generated_identifier(
fields: &[(String, Ident)], fields: &[(String, Ident, Vec<String>)],
cattrs: &attr::Container, cattrs: &attr::Container,
is_variant: bool, is_variant: bool,
other_idx: Option<usize>, other_idx: Option<usize>,
) -> Fragment { ) -> Fragment {
let this = quote!(__Field); let this = quote!(__Field);
let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident)| ident).collect(); let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident, _)| ident).collect();
let (ignore_variant, fallthrough) = if !is_variant && cattrs.has_flatten() { let (ignore_variant, fallthrough) = if !is_variant && cattrs.has_flatten() {
let ignore_variant = quote!(__other(_serde::private::de::Content<'de>),); let ignore_variant = quote!(__other(_serde::private::de::Content<'de>),);
@@ -1977,11 +1941,12 @@ fn deserialize_custom_identifier(
( (
variant.attrs.name().deserialize_name(), variant.attrs.name().deserialize_name(),
variant.ident.clone(), variant.ident.clone(),
variant.attrs.aliases(),
) )
}) })
.collect(); .collect();
let names = names_idents.iter().map(|&(ref name, _)| name); let names = names_idents.iter().map(|&(ref name, _, _)| name);
let names_const = if fallthrough.is_some() { let names_const = if fallthrough.is_some() {
None None
@@ -2032,24 +1997,33 @@ fn deserialize_custom_identifier(
fn deserialize_identifier( fn deserialize_identifier(
this: &TokenStream, this: &TokenStream,
fields: &[(String, Ident)], fields: &[(String, Ident, Vec<String>)],
is_variant: bool, is_variant: bool,
fallthrough: Option<TokenStream>, fallthrough: Option<TokenStream>,
collect_other_fields: bool, collect_other_fields: bool,
) -> Fragment { ) -> Fragment {
let field_strs = fields.iter().map(|&(ref name, _)| name); let mut flat_fields = Vec::new();
let field_borrowed_strs = fields.iter().map(|&(ref name, _)| name); for &(_, ref ident, ref aliases) in fields {
let field_bytes = fields flat_fields.extend(aliases.iter().map(|alias| (alias, ident)))
}
let field_strs = flat_fields.iter().map(|&(ref name, _)| name);
let field_borrowed_strs = flat_fields.iter().map(|&(ref name, _)| name);
let field_bytes = flat_fields
.iter() .iter()
.map(|&(ref name, _)| Literal::byte_string(name.as_bytes())); .map(|&(ref name, _)| Literal::byte_string(name.as_bytes()));
let field_borrowed_bytes = fields let field_borrowed_bytes = flat_fields
.iter() .iter()
.map(|&(ref name, _)| Literal::byte_string(name.as_bytes())); .map(|&(ref name, _)| Literal::byte_string(name.as_bytes()));
let constructors: &Vec<_> = &fields let constructors: &Vec<_> = &flat_fields
.iter() .iter()
.map(|&(_, ref ident)| quote!(#this::#ident)) .map(|&(_, ref ident)| quote!(#this::#ident))
.collect(); .collect();
let main_constructors: &Vec<_> = &fields
.iter()
.map(|&(_, ref ident, _)| quote!(#this::#ident))
.collect();
let expecting = if is_variant { let expecting = if is_variant {
"variant identifier" "variant identifier"
@@ -2237,7 +2211,7 @@ fn deserialize_identifier(
{ {
match __value { match __value {
#( #(
#variant_indices => _serde::export::Ok(#constructors), #variant_indices => _serde::export::Ok(#main_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),
@@ -2300,11 +2274,13 @@ fn deserialize_struct_as_struct_visitor(
.iter() .iter()
.enumerate() .enumerate()
.filter(|&(_, field)| !field.attrs.skip_deserializing()) .filter(|&(_, field)| !field.attrs.skip_deserializing())
.map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i))) .map(|(i, field)| {
(field.attrs.name().deserialize_name(), field_i(i), field.attrs.aliases())
})
.collect(); .collect();
let fields_stmt = { let fields_stmt = {
let field_names = field_names_idents.iter().map(|&(ref name, _)| name); let field_names = field_names_idents.iter().map(|&(ref name, _, _)| name);
quote_block! { quote_block! {
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
} }
@@ -2327,7 +2303,9 @@ fn deserialize_struct_as_map_visitor(
.iter() .iter()
.enumerate() .enumerate()
.filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
.map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i))) .map(|(i, field)| {
(field.attrs.name().deserialize_name(), field_i(i), field.attrs.aliases())
})
.collect(); .collect();
let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);
@@ -2558,11 +2536,13 @@ fn deserialize_struct_as_struct_in_place_visitor(
.iter() .iter()
.enumerate() .enumerate()
.filter(|&(_, field)| !field.attrs.skip_deserializing()) .filter(|&(_, field)| !field.attrs.skip_deserializing())
.map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i))) .map(|(i, field)| {
(field.attrs.name().deserialize_name(), field_i(i), field.attrs.aliases())
})
.collect(); .collect();
let fields_stmt = { let fields_stmt = {
let field_names = field_names_idents.iter().map(|&(ref name, _)| name); let field_names = field_names_idents.iter().map(|&(ref name, _, _)| name);
quote_block! { quote_block! {
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
} }
+147 -49
View File
@@ -20,7 +20,6 @@ use syn::NestedMeta::{Literal, Meta};
pub use internals::case::RenameRule; pub use internals::case::RenameRule;
#[derive(Clone)]
struct Attr<'c, T> { struct Attr<'c, T> {
cx: &'c Ctxt, cx: &'c Ctxt,
name: &'static str, name: &'static str,
@@ -90,9 +89,52 @@ impl<'c> BoolAttr<'c> {
} }
} }
struct VecAttr<'c, T> {
cx: &'c Ctxt,
name: &'static str,
first_dup_tokens: TokenStream,
values: Vec<T>,
}
impl<'c, T> VecAttr<'c, T> {
fn none(cx: &'c Ctxt, name: &'static str) -> Self {
VecAttr {
cx: cx,
name: name,
first_dup_tokens: TokenStream::new(),
values: Vec::new(),
}
}
fn insert<A: ToTokens>(&mut self, obj: A, value: T) {
if self.values.len() == 1 {
self.first_dup_tokens = obj.into_token_stream();
}
self.values.push(value);
}
fn at_most_one(mut self) -> Result<Option<T>, ()> {
if self.values.len() > 1 {
let dup_token = self.first_dup_tokens;
self.cx
.error_spanned_by(dup_token, format!("duplicate serde attribute `{}`", self.name));
Err(())
} else {
Ok(self.values.pop())
}
}
fn get(self) -> Vec<T> {
self.values
}
}
pub struct Name { pub struct Name {
serialize: String, serialize: String,
serialize_renamed: bool,
deserialize: String, deserialize: String,
deserialize_renamed: bool,
deserialize_aliases: Vec<String>,
} }
#[allow(deprecated)] #[allow(deprecated)]
@@ -104,6 +146,36 @@ fn unraw(ident: &Ident) -> String {
} }
impl Name { impl Name {
fn from_attrs(
source_name: String,
ser_name: Attr<String>,
de_name: Attr<String>,
de_aliases: Option<VecAttr<String>>,
) -> Name {
let deserialize_aliases = match de_aliases {
Some(de_aliases) => {
let mut alias_list = BTreeSet::new();
for alias_name in de_aliases.get() {
alias_list.insert(alias_name);
}
alias_list.into_iter().collect()
}
None => Vec::new(),
};
let ser_name = ser_name.get();
let ser_renamed = ser_name.is_some();
let de_name = de_name.get();
let de_renamed = de_name.is_some();
Name {
serialize: ser_name.unwrap_or_else(|| source_name.clone()),
serialize_renamed: ser_renamed,
deserialize: de_name.unwrap_or(source_name),
deserialize_renamed: de_renamed,
deserialize_aliases: deserialize_aliases,
}
}
/// Return the container name for the container when serializing. /// Return the container name for the container when serializing.
pub fn serialize_name(&self) -> String { pub fn serialize_name(&self) -> String {
self.serialize.clone() self.serialize.clone()
@@ -113,6 +185,15 @@ impl Name {
pub fn deserialize_name(&self) -> String { pub fn deserialize_name(&self) -> String {
self.deserialize.clone() self.deserialize.clone()
} }
fn deserialize_aliases(&self) -> Vec<String> {
let mut aliases = self.deserialize_aliases.clone();
let main_name = self.deserialize_name();
if !aliases.contains(&main_name) {
aliases.push(main_name);
}
aliases
}
} }
pub struct RenameAllRules { pub struct RenameAllRules {
@@ -514,10 +595,7 @@ impl Container {
} }
Container { Container {
name: Name { name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None),
serialize: ser_name.get().unwrap_or_else(|| unraw(&item.ident)),
deserialize: de_name.get().unwrap_or_else(|| unraw(&item.ident)),
},
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),
@@ -762,8 +840,6 @@ fn decide_identifier(
/// Represents variant attribute information /// Represents variant attribute information
pub struct Variant { pub struct Variant {
name: Name, name: Name,
ser_renamed: bool,
de_renamed: bool,
rename_all_rules: RenameAllRules, 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>>,
@@ -779,6 +855,7 @@ impl Variant {
pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self { pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> 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 de_aliases = VecAttr::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_ser_rule = Attr::none(cx, "rename_all"); let mut rename_all_ser_rule = Attr::none(cx, "rename_all");
@@ -797,15 +874,26 @@ impl Variant {
Meta(NameValue(ref m)) if m.ident == "rename" => { Meta(NameValue(ref m)) if m.ident == "rename" => {
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) {
ser_name.set(&m.ident, s.value()); ser_name.set(&m.ident, s.value());
de_name.set(&m.ident, s.value()); de_name.set_if_none(s.value());
de_aliases.insert(&m.ident, s.value());
} }
} }
// Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]` // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
Meta(List(ref m)) if m.ident == "rename" => { Meta(List(ref m)) if m.ident == "rename" => {
if let Ok((ser, de)) = get_renames(cx, &m.nested) { if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) {
ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value)); ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value));
de_name.set_opt(&m.ident, de.map(syn::LitStr::value)); for de_value in de {
de_name.set_if_none(de_value.value());
de_aliases.insert(&m.ident, de_value.value());
}
}
}
// Parse `#[serde(alias = "foo")]`
Meta(NameValue(ref m)) if m.ident == "alias" => {
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
de_aliases.insert(&m.ident, s.value());
} }
} }
@@ -963,17 +1051,8 @@ impl Variant {
} }
} }
let ser_name = ser_name.get();
let ser_renamed = ser_name.is_some();
let de_name = de_name.get();
let de_renamed = de_name.is_some();
Variant { Variant {
name: Name { name: Name::from_attrs(unraw(&variant.ident), ser_name, de_name, Some(de_aliases)),
serialize: ser_name.unwrap_or_else(|| unraw(&variant.ident)),
deserialize: de_name.unwrap_or_else(|| unraw(&variant.ident)),
},
ser_renamed: ser_renamed,
de_renamed: de_renamed,
rename_all_rules: RenameAllRules { rename_all_rules: RenameAllRules {
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None), serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None), deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
@@ -993,11 +1072,15 @@ impl Variant {
&self.name &self.name
} }
pub fn aliases(&self) -> Vec<String> {
self.name.deserialize_aliases()
}
pub fn rename_by_rules(&mut self, rules: &RenameAllRules) { pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
if !self.ser_renamed { if !self.name.serialize_renamed {
self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize); self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize);
} }
if !self.de_renamed { if !self.name.deserialize_renamed {
self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize); self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize);
} }
} }
@@ -1038,8 +1121,6 @@ impl Variant {
/// Represents field attribute information /// Represents field attribute information
pub struct Field { pub struct Field {
name: Name, name: Name,
ser_renamed: bool,
de_renamed: bool,
skip_serializing: bool, skip_serializing: bool,
skip_deserializing: bool, skip_deserializing: bool,
skip_serializing_if: Option<syn::ExprPath>, skip_serializing_if: Option<syn::ExprPath>,
@@ -1084,6 +1165,7 @@ impl Field {
) -> Self { ) -> 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 de_aliases = VecAttr::none(cx, "rename");
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing"); let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing"); let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
let mut skip_serializing_if = Attr::none(cx, "skip_serializing_if"); let mut skip_serializing_if = Attr::none(cx, "skip_serializing_if");
@@ -1117,15 +1199,26 @@ impl Field {
Meta(NameValue(ref m)) if m.ident == "rename" => { Meta(NameValue(ref m)) if m.ident == "rename" => {
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) {
ser_name.set(&m.ident, s.value()); ser_name.set(&m.ident, s.value());
de_name.set(&m.ident, s.value()); de_name.set_if_none(s.value());
de_aliases.insert(&m.ident, s.value());
} }
} }
// Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]` // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
Meta(List(ref m)) if m.ident == "rename" => { Meta(List(ref m)) if m.ident == "rename" => {
if let Ok((ser, de)) = get_renames(cx, &m.nested) { if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) {
ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value)); ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value));
de_name.set_opt(&m.ident, de.map(syn::LitStr::value)); for de_value in de {
de_name.set_if_none(de_value.value());
de_aliases.insert(&m.ident, de_value.value());
}
}
}
// Parse `#[serde(alias = "foo")]`
Meta(NameValue(ref m)) if m.ident == "alias" => {
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
de_aliases.insert(&m.ident, s.value());
} }
} }
@@ -1332,17 +1425,8 @@ impl Field {
collect_lifetimes(&field.ty, &mut borrowed_lifetimes); collect_lifetimes(&field.ty, &mut borrowed_lifetimes);
} }
let ser_name = ser_name.get();
let ser_renamed = ser_name.is_some();
let de_name = de_name.get();
let de_renamed = de_name.is_some();
Field { Field {
name: Name { name: Name::from_attrs(ident, ser_name, de_name, Some(de_aliases)),
serialize: ser_name.unwrap_or_else(|| ident.clone()),
deserialize: de_name.unwrap_or(ident),
},
ser_renamed: ser_renamed,
de_renamed: de_renamed,
skip_serializing: skip_serializing.get(), skip_serializing: skip_serializing.get(),
skip_deserializing: skip_deserializing.get(), skip_deserializing: skip_deserializing.get(),
skip_serializing_if: skip_serializing_if.get(), skip_serializing_if: skip_serializing_if.get(),
@@ -1362,11 +1446,15 @@ impl Field {
&self.name &self.name
} }
pub fn aliases(&self) -> Vec<String> {
self.name.deserialize_aliases()
}
pub fn rename_by_rules(&mut self, rules: &RenameAllRules) { pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
if !self.ser_renamed { if !self.name.serialize_renamed {
self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize); self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize);
} }
if !self.de_renamed { if !self.name.deserialize_renamed {
self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize); self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize);
} }
} }
@@ -1426,31 +1514,31 @@ impl Field {
type SerAndDe<T> = (Option<T>, Option<T>); type SerAndDe<T> = (Option<T>, Option<T>);
fn get_ser_and_de<'a, T, F>( fn get_ser_and_de<'a, 'b, T, F>(
cx: &Ctxt, cx: &'b Ctxt,
attr_name: &'static str, attr_name: &'static str,
metas: &'a Punctuated<syn::NestedMeta, Token![,]>, metas: &'a Punctuated<syn::NestedMeta, Token![,]>,
f: F, f: F,
) -> Result<SerAndDe<T>, ()> ) -> Result<(VecAttr<'b, T>, VecAttr<'b, T>), ()>
where where
T: 'a, T: 'a,
F: Fn(&Ctxt, &Ident, &Ident, &'a syn::Lit) -> Result<T, ()>, F: Fn(&Ctxt, &Ident, &Ident, &'a syn::Lit) -> Result<T, ()>,
{ {
let mut ser_meta = Attr::none(cx, attr_name); let mut ser_meta = VecAttr::none(cx, attr_name);
let mut de_meta = Attr::none(cx, attr_name); let mut de_meta = VecAttr::none(cx, attr_name);
let attr_name = Ident::new(attr_name, Span::call_site()); let attr_name = Ident::new(attr_name, Span::call_site());
for meta in metas { for meta in metas {
match *meta { match *meta {
Meta(NameValue(ref meta)) if meta.ident == "serialize" => { Meta(NameValue(ref meta)) if meta.ident == "serialize" => {
if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) { if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) {
ser_meta.set(&meta.ident, v); ser_meta.insert(&meta.ident, v);
} }
} }
Meta(NameValue(ref meta)) if meta.ident == "deserialize" => { Meta(NameValue(ref meta)) if meta.ident == "deserialize" => {
if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) { if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) {
de_meta.set(&meta.ident, v); de_meta.insert(&meta.ident, v);
} }
} }
@@ -1468,21 +1556,31 @@ where
} }
} }
Ok((ser_meta.get(), de_meta.get())) Ok((ser_meta, de_meta))
} }
fn get_renames<'a>( fn get_renames<'a>(
cx: &Ctxt, cx: &Ctxt,
items: &'a Punctuated<syn::NestedMeta, Token![,]>, items: &'a Punctuated<syn::NestedMeta, Token![,]>,
) -> Result<SerAndDe<&'a syn::LitStr>, ()> { ) -> Result<SerAndDe<&'a syn::LitStr>, ()> {
get_ser_and_de(cx, "rename", items, get_lit_str) let (ser, de) = try!(get_ser_and_de(cx, "rename", items, get_lit_str));
Ok((try!(ser.at_most_one()), try!(de.at_most_one())))
}
fn get_multiple_renames<'a>(
cx: &Ctxt,
items: &'a Punctuated<syn::NestedMeta, Token![,]>,
) -> Result<(Option<&'a syn::LitStr>, Vec<&'a syn::LitStr>), ()> {
let (ser, de) = try!(get_ser_and_de(cx, "rename", items, get_lit_str));
Ok((try!(ser.at_most_one()), de.get()))
} }
fn get_where_predicates( fn get_where_predicates(
cx: &Ctxt, cx: &Ctxt,
items: &Punctuated<syn::NestedMeta, Token![,]>, items: &Punctuated<syn::NestedMeta, Token![,]>,
) -> Result<SerAndDe<Vec<syn::WherePredicate>>, ()> { ) -> Result<SerAndDe<Vec<syn::WherePredicate>>, ()> {
get_ser_and_de(cx, "bound", items, parse_lit_into_where) let (ser, de) = try!(get_ser_and_de(cx, "bound", items, parse_lit_into_where));
Ok((try!(ser.at_most_one()), try!(de.at_most_one())))
} }
pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> { pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
+8 -2
View File
@@ -295,12 +295,18 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
let check_de = !field.attrs.skip_deserializing(); let check_de = !field.attrs.skip_deserializing();
let name = field.attrs.name(); let name = field.attrs.name();
let ser_name = name.serialize_name(); let ser_name = name.serialize_name();
let de_name = name.deserialize_name();
if check_ser && ser_name == tag || check_de && de_name == tag { if check_ser && ser_name == tag {
diagnose_conflict(); diagnose_conflict();
return; return;
} }
for de_name in field.attrs.aliases() {
if check_de && de_name == tag {
diagnose_conflict();
return;
}
}
} }
} }
Style::Unit | Style::Newtype | Style::Tuple => {} Style::Unit | Style::Newtype | Style::Tuple => {}
+1 -1
View File
@@ -13,7 +13,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.84")] #![doc(html_root_url = "https://docs.rs/serde_derive/1.0.85")]
#![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))]
// Ignored clippy lints // Ignored clippy lints
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_test" name = "serde_test"
version = "1.0.84" # remember to update html_root_url version = "1.0.85" # 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
@@ -144,7 +144,7 @@
//! # } //! # }
//! ``` //! ```
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.84")] #![doc(html_root_url = "https://docs.rs/serde_test/1.0.85")]
#![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))]
// Ignored clippy lints // Ignored clippy lints
+2 -4
View File
@@ -4,7 +4,7 @@ use compiletest_rs as compiletest;
#[test] #[test]
fn ui() { fn ui() {
let config = compiletest::Config { compiletest::run_tests(&compiletest::Config {
mode: compiletest::common::Mode::Ui, mode: compiletest::common::Mode::Ui,
src_base: std::path::PathBuf::from("tests/ui"), src_base: std::path::PathBuf::from("tests/ui"),
target_rustcflags: Some(String::from( target_rustcflags: Some(String::from(
@@ -16,7 +16,5 @@ fn ui() {
", ",
)), )),
..Default::default() ..Default::default()
}; });
compiletest::run_tests(&config);
} }
+151
View File
@@ -516,6 +516,16 @@ struct RenameStructSerializeDeserialize {
a2: i32, a2: i32,
} }
#[derive(Debug, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
struct AliasStruct {
a1: i32,
#[serde(alias = "a3")]
a2: i32,
#[serde(alias = "a5", rename = "a6")]
a4: i32,
}
#[test] #[test]
fn test_rename_struct() { fn test_rename_struct() {
assert_tokens( assert_tokens(
@@ -562,6 +572,59 @@ fn test_rename_struct() {
Token::StructEnd, Token::StructEnd,
], ],
); );
assert_de_tokens(
&AliasStruct { a1: 1, a2: 2, a4: 3 },
&[
Token::Struct {
name: "AliasStruct",
len: 3,
},
Token::Str("a1"),
Token::I32(1),
Token::Str("a2"),
Token::I32(2),
Token::Str("a5"),
Token::I32(3),
Token::StructEnd,
],
);
assert_de_tokens(
&AliasStruct { a1: 1, a2: 2, a4: 3 },
&[
Token::Struct {
name: "AliasStruct",
len: 3,
},
Token::Str("a1"),
Token::I32(1),
Token::Str("a3"),
Token::I32(2),
Token::Str("a6"),
Token::I32(3),
Token::StructEnd,
],
);
}
#[test]
fn test_unknown_field_rename_struct() {
assert_de_tokens_error::<AliasStruct>(
&[
Token::Struct {
name: "AliasStruct",
len: 3,
},
Token::Str("a1"),
Token::I32(1),
Token::Str("a3"),
Token::I32(2),
Token::Str("a4"),
Token::I32(3),
],
"unknown field `a4`, expected one of `a1`, `a2`, `a6`",
);
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
@@ -592,6 +655,19 @@ enum RenameEnumSerializeDeserialize<A> {
}, },
} }
#[derive(Debug, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
enum AliasEnum {
#[serde(rename = "sailor_moon", alias = "usagi_tsukino")]
SailorMoon {
a: i8,
#[serde(alias = "c")]
b: i8,
#[serde(alias = "e", rename = "f")]
d: i8,
}
}
#[test] #[test]
fn test_rename_enum() { fn test_rename_enum() {
assert_tokens( assert_tokens(
@@ -678,6 +754,81 @@ fn test_rename_enum() {
Token::StructVariantEnd, Token::StructVariantEnd,
], ],
); );
assert_de_tokens(
&AliasEnum::SailorMoon {
a: 0,
b: 1,
d: 2,
},
&[
Token::StructVariant {
name: "AliasEnum",
variant: "sailor_moon",
len: 3,
},
Token::Str("a"),
Token::I8(0),
Token::Str("b"),
Token::I8(1),
Token::Str("e"),
Token::I8(2),
Token::StructVariantEnd,
],
);
assert_de_tokens(
&AliasEnum::SailorMoon {
a: 0,
b: 1,
d: 2,
},
&[
Token::StructVariant {
name: "AliasEnum",
variant: "usagi_tsukino",
len: 3,
},
Token::Str("a"),
Token::I8(0),
Token::Str("c"),
Token::I8(1),
Token::Str("f"),
Token::I8(2),
Token::StructVariantEnd,
],
);
}
#[test]
fn test_unknown_field_rename_enum() {
assert_de_tokens_error::<AliasEnum>(
&[
Token::StructVariant {
name: "AliasEnum",
variant: "SailorMoon",
len: 3,
},
],
"unknown variant `SailorMoon`, expected `sailor_moon`",
);
assert_de_tokens_error::<AliasEnum>(
&[
Token::StructVariant {
name: "AliasEnum",
variant: "usagi_tsukino",
len: 3,
},
Token::Str("a"),
Token::I8(0),
Token::Str("c"),
Token::I8(1),
Token::Str("d"),
Token::I8(2),
],
"unknown field `d`, expected one of `a`, `b`, `f`",
);
} }
#[derive(Debug, PartialEq, Serialize)] #[derive(Debug, PartialEq, Serialize)]
@@ -0,0 +1,12 @@
use serde_derive::Serialize;
#[derive(Serialize)]
#[serde(tag = "conflict")]
enum E {
A {
#[serde(alias = "conflict")]
x: (),
},
}
fn main() {}
@@ -0,0 +1,14 @@
error: variant field name `conflict` conflicts with internal tag
--> $DIR/internal-tag-alias.rs:4:1
|
4 | / #[serde(tag = "conflict")]
5 | | enum E {
6 | | A {
7 | | #[serde(alias = "conflict")]
8 | | x: (),
9 | | },
10 | | }
| |_^
error: aborting due to previous error
@@ -1,10 +0,0 @@
use serde_derive::Serialize;
#[derive(Serialize)]
struct S {
#[serde(rename = "x")]
#[serde(rename(deserialize = "y"))]
x: (),
}
fn main() {}
@@ -1,8 +0,0 @@
error: duplicate serde attribute `rename`
--> $DIR/rename-rename-de.rs:6:13
|
6 | #[serde(rename(deserialize = "y"))]
| ^^^^^^
error: aborting due to previous error