Implements alias annotation and allow multiple deserialization renames.
This commit is contained in:
parent
b605cd1bb9
commit
1ed228b92b
@ -1140,25 +1140,17 @@ fn deserialize_enum(
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_externally_tagged_enum(
|
||||
params: &Parameters,
|
||||
fn prepare_enum_variant_enum(
|
||||
variants: &[Variant],
|
||||
cattrs: &attr::Container,
|
||||
) -> Fragment {
|
||||
let this = ¶ms.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());
|
||||
|
||||
) -> (proc_macro2::TokenStream, Stmts) {
|
||||
let variant_names_idents: Vec<_> = variants
|
||||
.iter()
|
||||
.enumerate()
|
||||
.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();
|
||||
|
||||
let other_idx = variants
|
||||
@ -1166,7 +1158,7 @@ fn deserialize_externally_tagged_enum(
|
||||
.position(|ref variant| variant.attrs.other());
|
||||
|
||||
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! {
|
||||
const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
|
||||
}
|
||||
@ -1179,6 +1171,24 @@ fn deserialize_externally_tagged_enum(
|
||||
other_idx,
|
||||
));
|
||||
|
||||
(variants_stmt, variant_visitor)
|
||||
}
|
||||
|
||||
fn deserialize_externally_tagged_enum(
|
||||
params: &Parameters,
|
||||
variants: &[Variant],
|
||||
cattrs: &attr::Container,
|
||||
) -> Fragment {
|
||||
let this = ¶ms.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
|
||||
let variant_arms = variants
|
||||
.iter()
|
||||
@ -1261,30 +1271,7 @@ fn deserialize_internally_tagged_enum(
|
||||
cattrs: &attr::Container,
|
||||
tag: &str,
|
||||
) -> Fragment {
|
||||
let variant_names_idents: Vec<_> = variants
|
||||
.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 (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
|
||||
|
||||
// Match arms to extract a variant from a string
|
||||
let variant_arms = variants
|
||||
@ -1335,30 +1322,7 @@ fn deserialize_adjacently_tagged_enum(
|
||||
split_with_de_lifetime(params);
|
||||
let delife = params.borrowed.de_lifetime();
|
||||
|
||||
let variant_names_idents: Vec<_> = variants
|
||||
.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 (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
|
||||
|
||||
let variant_arms: &Vec<_> = &variants
|
||||
.iter()
|
||||
@ -1870,13 +1834,13 @@ fn deserialize_untagged_newtype_variant(
|
||||
}
|
||||
|
||||
fn deserialize_generated_identifier(
|
||||
fields: &[(String, Ident)],
|
||||
fields: &[(String, Ident, Vec<String>)],
|
||||
cattrs: &attr::Container,
|
||||
is_variant: bool,
|
||||
other_idx: Option<usize>,
|
||||
) -> Fragment {
|
||||
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 = quote!(__other(_serde::private::de::Content<'de>),);
|
||||
@ -1977,11 +1941,12 @@ fn deserialize_custom_identifier(
|
||||
(
|
||||
variant.attrs.name().deserialize_name(),
|
||||
variant.ident.clone(),
|
||||
variant.attrs.aliases(),
|
||||
)
|
||||
})
|
||||
.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() {
|
||||
None
|
||||
@ -2032,24 +1997,33 @@ fn deserialize_custom_identifier(
|
||||
|
||||
fn deserialize_identifier(
|
||||
this: &TokenStream,
|
||||
fields: &[(String, Ident)],
|
||||
fields: &[(String, Ident, Vec<String>)],
|
||||
is_variant: bool,
|
||||
fallthrough: Option<TokenStream>,
|
||||
collect_other_fields: bool,
|
||||
) -> Fragment {
|
||||
let field_strs = fields.iter().map(|&(ref name, _)| name);
|
||||
let field_borrowed_strs = fields.iter().map(|&(ref name, _)| name);
|
||||
let field_bytes = fields
|
||||
let mut flat_fields = Vec::new();
|
||||
for &(_, ref ident, ref aliases) in fields {
|
||||
flat_fields.extend(aliases.into_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()
|
||||
.map(|&(ref name, _)| Literal::byte_string(name.as_bytes()));
|
||||
let field_borrowed_bytes = fields
|
||||
let field_borrowed_bytes = flat_fields
|
||||
.iter()
|
||||
.map(|&(ref name, _)| Literal::byte_string(name.as_bytes()));
|
||||
|
||||
let constructors: &Vec<_> = &fields
|
||||
let constructors: &Vec<_> = &flat_fields
|
||||
.iter()
|
||||
.map(|&(_, ref ident)| quote!(#this::#ident))
|
||||
.collect();
|
||||
let main_constructors: &Vec<_> = &fields
|
||||
.iter()
|
||||
.map(|&(_, ref ident, _)| quote!(#this::#ident))
|
||||
.collect();
|
||||
|
||||
let expecting = if is_variant {
|
||||
"variant identifier"
|
||||
@ -2237,7 +2211,7 @@ fn deserialize_identifier(
|
||||
{
|
||||
match __value {
|
||||
#(
|
||||
#variant_indices => _serde::export::Ok(#constructors),
|
||||
#variant_indices => _serde::export::Ok(#main_constructors),
|
||||
)*
|
||||
_ => _serde::export::Err(_serde::de::Error::invalid_value(
|
||||
_serde::de::Unexpected::Unsigned(__value),
|
||||
@ -2300,11 +2274,13 @@ fn deserialize_struct_as_struct_visitor(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.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();
|
||||
|
||||
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! {
|
||||
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
|
||||
}
|
||||
@ -2327,7 +2303,9 @@ fn deserialize_struct_as_map_visitor(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.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();
|
||||
|
||||
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()
|
||||
.enumerate()
|
||||
.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();
|
||||
|
||||
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! {
|
||||
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
|
||||
}
|
||||
|
@ -90,9 +90,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.drain(..).next())
|
||||
}
|
||||
}
|
||||
|
||||
fn get(self) -> Vec<T> {
|
||||
self.values
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Name {
|
||||
serialize: String,
|
||||
serialize_renamed: bool,
|
||||
deserialize: String,
|
||||
deserialize_renamed: bool,
|
||||
deserialize_aliases: Vec<String>,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
@ -104,6 +147,36 @@ fn unraw(ident: &Ident) -> String {
|
||||
}
|
||||
|
||||
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.
|
||||
pub fn serialize_name(&self) -> String {
|
||||
self.serialize.clone()
|
||||
@ -113,6 +186,15 @@ impl Name {
|
||||
pub fn deserialize_name(&self) -> String {
|
||||
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 {
|
||||
@ -514,10 +596,7 @@ impl Container {
|
||||
}
|
||||
|
||||
Container {
|
||||
name: Name {
|
||||
serialize: ser_name.get().unwrap_or_else(|| unraw(&item.ident)),
|
||||
deserialize: de_name.get().unwrap_or_else(|| unraw(&item.ident)),
|
||||
},
|
||||
name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None),
|
||||
transparent: transparent.get(),
|
||||
deny_unknown_fields: deny_unknown_fields.get(),
|
||||
default: default.get().unwrap_or(Default::None),
|
||||
@ -762,8 +841,6 @@ fn decide_identifier(
|
||||
/// Represents variant attribute information
|
||||
pub struct Variant {
|
||||
name: Name,
|
||||
ser_renamed: bool,
|
||||
de_renamed: bool,
|
||||
rename_all_rules: RenameAllRules,
|
||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||
@ -779,6 +856,7 @@ impl Variant {
|
||||
pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self {
|
||||
let mut ser_name = Attr::none(cx, "rename");
|
||||
let mut de_name = Attr::none(cx, "rename");
|
||||
let mut de_alises = VecAttr::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_ser_rule = Attr::none(cx, "rename_all");
|
||||
@ -797,15 +875,26 @@ impl Variant {
|
||||
Meta(NameValue(ref m)) if m.ident == "rename" => {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
ser_name.set(&m.ident, s.value());
|
||||
de_name.set(&m.ident, s.value());
|
||||
de_name.set_if_none(s.value());
|
||||
de_alises.insert(&m.ident, s.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
|
||||
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));
|
||||
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_alises.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_alises.insert(&m.ident, s.value());
|
||||
}
|
||||
}
|
||||
|
||||
@ -963,17 +1052,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 {
|
||||
name: Name {
|
||||
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,
|
||||
name: Name::from_attrs(unraw(&variant.ident), ser_name, de_name, Some(de_alises)),
|
||||
rename_all_rules: RenameAllRules {
|
||||
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
|
||||
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
|
||||
@ -993,11 +1073,15 @@ impl Variant {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn aliases(&self) -> Vec<String> {
|
||||
self.name.deserialize_aliases()
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if !self.de_renamed {
|
||||
if !self.name.deserialize_renamed {
|
||||
self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize);
|
||||
}
|
||||
}
|
||||
@ -1038,8 +1122,6 @@ impl Variant {
|
||||
/// Represents field attribute information
|
||||
pub struct Field {
|
||||
name: Name,
|
||||
ser_renamed: bool,
|
||||
de_renamed: bool,
|
||||
skip_serializing: bool,
|
||||
skip_deserializing: bool,
|
||||
skip_serializing_if: Option<syn::ExprPath>,
|
||||
@ -1084,6 +1166,7 @@ impl Field {
|
||||
) -> Self {
|
||||
let mut ser_name = Attr::none(cx, "rename");
|
||||
let mut de_name = Attr::none(cx, "rename");
|
||||
let mut de_alises = VecAttr::none(cx, "rename");
|
||||
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
||||
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
|
||||
let mut skip_serializing_if = Attr::none(cx, "skip_serializing_if");
|
||||
@ -1117,15 +1200,26 @@ impl Field {
|
||||
Meta(NameValue(ref m)) if m.ident == "rename" => {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
ser_name.set(&m.ident, s.value());
|
||||
de_name.set(&m.ident, s.value());
|
||||
de_name.set_if_none(s.value());
|
||||
de_alises.insert(&m.ident, s.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
|
||||
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));
|
||||
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_alises.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_alises.insert(&m.ident, s.value());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1332,17 +1426,8 @@ impl Field {
|
||||
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 {
|
||||
name: Name {
|
||||
serialize: ser_name.unwrap_or_else(|| ident.clone()),
|
||||
deserialize: de_name.unwrap_or(ident),
|
||||
},
|
||||
ser_renamed: ser_renamed,
|
||||
de_renamed: de_renamed,
|
||||
name: Name::from_attrs(ident, ser_name, de_name, Some(de_alises)),
|
||||
skip_serializing: skip_serializing.get(),
|
||||
skip_deserializing: skip_deserializing.get(),
|
||||
skip_serializing_if: skip_serializing_if.get(),
|
||||
@ -1362,11 +1447,15 @@ impl Field {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn aliases(&self) -> Vec<String> {
|
||||
self.name.deserialize_aliases()
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if !self.de_renamed {
|
||||
if !self.name.deserialize_renamed {
|
||||
self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize);
|
||||
}
|
||||
}
|
||||
@ -1426,31 +1515,31 @@ impl Field {
|
||||
|
||||
type SerAndDe<T> = (Option<T>, Option<T>);
|
||||
|
||||
fn get_ser_and_de<'a, T, F>(
|
||||
cx: &Ctxt,
|
||||
fn get_ser_and_de<'a, 'b, T, F>(
|
||||
cx: &'b Ctxt,
|
||||
attr_name: &'static str,
|
||||
metas: &'a Punctuated<syn::NestedMeta, Token![,]>,
|
||||
f: F,
|
||||
) -> Result<SerAndDe<T>, ()>
|
||||
) -> Result<(VecAttr<'b, T>, VecAttr<'b, T>), ()>
|
||||
where
|
||||
T: 'a,
|
||||
F: Fn(&Ctxt, &Ident, &Ident, &'a syn::Lit) -> Result<T, ()>,
|
||||
{
|
||||
let mut ser_meta = Attr::none(cx, attr_name);
|
||||
let mut de_meta = Attr::none(cx, attr_name);
|
||||
let mut ser_meta = VecAttr::none(cx, attr_name);
|
||||
let mut de_meta = VecAttr::none(cx, attr_name);
|
||||
let attr_name = Ident::new(attr_name, Span::call_site());
|
||||
|
||||
for meta in metas {
|
||||
match *meta {
|
||||
Meta(NameValue(ref meta)) if meta.ident == "serialize" => {
|
||||
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" => {
|
||||
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 +1557,31 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
Ok((ser_meta.get(), de_meta.get()))
|
||||
Ok((ser_meta, de_meta))
|
||||
}
|
||||
|
||||
fn get_renames<'a>(
|
||||
cx: &Ctxt,
|
||||
items: &'a Punctuated<syn::NestedMeta, Token![,]>,
|
||||
) -> 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(
|
||||
cx: &Ctxt,
|
||||
items: &Punctuated<syn::NestedMeta, Token![,]>,
|
||||
) -> 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>> {
|
||||
|
@ -295,12 +295,18 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
|
||||
let check_de = !field.attrs.skip_deserializing();
|
||||
let name = field.attrs.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();
|
||||
return;
|
||||
}
|
||||
|
||||
for de_name in field.attrs.aliases() {
|
||||
if check_de && de_name == tag {
|
||||
diagnose_conflict();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Style::Unit | Style::Newtype | Style::Tuple => {}
|
||||
|
@ -516,6 +516,16 @@ struct RenameStructSerializeDeserialize {
|
||||
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]
|
||||
fn test_rename_struct() {
|
||||
assert_tokens(
|
||||
@ -562,6 +572,59 @@ fn test_rename_struct() {
|
||||
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)]
|
||||
@ -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]
|
||||
fn test_rename_enum() {
|
||||
assert_tokens(
|
||||
@ -678,6 +754,81 @@ fn test_rename_enum() {
|
||||
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)]
|
||||
|
12
test_suite/tests/ui/conflict/internal-tag-alias.rs
Normal file
12
test_suite/tests/ui/conflict/internal-tag-alias.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use serde_derive::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "conflict")]
|
||||
enum E {
|
||||
A {
|
||||
#[serde(alias = "conflict")]
|
||||
x: (),
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {}
|
14
test_suite/tests/ui/conflict/internal-tag-alias.stderr
Normal file
14
test_suite/tests/ui/conflict/internal-tag-alias.stderr
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user