Merge pull request #2458 from Mingun/identifier
Keep aliases always sorted and include aliases in expecting message for field/variant_identifier
This commit is contained in:
commit
431636af0d
@ -2131,7 +2131,7 @@ fn deserialize_custom_identifier(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let names = names_idents.iter().map(|(name, _, _)| name);
|
let names = names_idents.iter().flat_map(|(_, _, aliases)| aliases);
|
||||||
|
|
||||||
let names_const = if fallthrough.is_some() {
|
let names_const = if fallthrough.is_some() {
|
||||||
None
|
None
|
||||||
@ -2194,25 +2194,17 @@ fn deserialize_identifier(
|
|||||||
collect_other_fields: bool,
|
collect_other_fields: bool,
|
||||||
expecting: Option<&str>,
|
expecting: Option<&str>,
|
||||||
) -> Fragment {
|
) -> Fragment {
|
||||||
let mut flat_fields = Vec::new();
|
let str_mapping = fields.iter().map(|(_, ident, aliases)| {
|
||||||
for (_, ident, aliases) in fields {
|
// `aliases` also contains a main name
|
||||||
flat_fields.extend(aliases.iter().map(|alias| (alias, ident)));
|
quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident))
|
||||||
}
|
});
|
||||||
|
let bytes_mapping = fields.iter().map(|(_, ident, aliases)| {
|
||||||
let field_strs: &Vec<_> = &flat_fields.iter().map(|(name, _)| name).collect();
|
// `aliases` also contains a main name
|
||||||
let field_bytes: &Vec<_> = &flat_fields
|
let aliases = aliases
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, _)| Literal::byte_string(name.as_bytes()))
|
.map(|alias| Literal::byte_string(alias.as_bytes()));
|
||||||
.collect();
|
quote!(#(#aliases)|* => _serde::__private::Ok(#this_value::#ident))
|
||||||
|
});
|
||||||
let constructors: &Vec<_> = &flat_fields
|
|
||||||
.iter()
|
|
||||||
.map(|(_, ident)| quote!(#this_value::#ident))
|
|
||||||
.collect();
|
|
||||||
let main_constructors: &Vec<_> = &fields
|
|
||||||
.iter()
|
|
||||||
.map(|(_, ident, _)| quote!(#this_value::#ident))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let expecting = expecting.unwrap_or(if is_variant {
|
let expecting = expecting.unwrap_or(if is_variant {
|
||||||
"variant identifier"
|
"variant identifier"
|
||||||
@ -2220,8 +2212,6 @@ fn deserialize_identifier(
|
|||||||
"field identifier"
|
"field identifier"
|
||||||
});
|
});
|
||||||
|
|
||||||
let index_expecting = if is_variant { "variant" } else { "field" };
|
|
||||||
|
|
||||||
let bytes_to_str = if fallthrough.is_some() || collect_other_fields {
|
let bytes_to_str = if fallthrough.is_some() || collect_other_fields {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -2269,21 +2259,6 @@ fn deserialize_identifier(
|
|||||||
&fallthrough_arm_tokens
|
&fallthrough_arm_tokens
|
||||||
};
|
};
|
||||||
|
|
||||||
let u64_fallthrough_arm_tokens;
|
|
||||||
let u64_fallthrough_arm = if let Some(fallthrough) = &fallthrough {
|
|
||||||
fallthrough
|
|
||||||
} else {
|
|
||||||
let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len());
|
|
||||||
u64_fallthrough_arm_tokens = quote! {
|
|
||||||
_serde::__private::Err(_serde::de::Error::invalid_value(
|
|
||||||
_serde::de::Unexpected::Unsigned(__value),
|
|
||||||
&#fallthrough_msg,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
&u64_fallthrough_arm_tokens
|
|
||||||
};
|
|
||||||
|
|
||||||
let variant_indices = 0_u64..;
|
|
||||||
let visit_other = if collect_other_fields {
|
let visit_other = if collect_other_fields {
|
||||||
quote! {
|
quote! {
|
||||||
fn visit_bool<__E>(self, __value: bool) -> _serde::__private::Result<Self::Value, __E>
|
fn visit_bool<__E>(self, __value: bool) -> _serde::__private::Result<Self::Value, __E>
|
||||||
@ -2378,15 +2353,33 @@ fn deserialize_identifier(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let u64_mapping = fields.iter().enumerate().map(|(i, (_, ident, _))| {
|
||||||
|
let i = i as u64;
|
||||||
|
quote!(#i => _serde::__private::Ok(#this_value::#ident))
|
||||||
|
});
|
||||||
|
|
||||||
|
let u64_fallthrough_arm_tokens;
|
||||||
|
let u64_fallthrough_arm = if let Some(fallthrough) = &fallthrough {
|
||||||
|
fallthrough
|
||||||
|
} else {
|
||||||
|
let index_expecting = if is_variant { "variant" } else { "field" };
|
||||||
|
let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len());
|
||||||
|
u64_fallthrough_arm_tokens = quote! {
|
||||||
|
_serde::__private::Err(_serde::de::Error::invalid_value(
|
||||||
|
_serde::de::Unexpected::Unsigned(__value),
|
||||||
|
&#fallthrough_msg,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
&u64_fallthrough_arm_tokens
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
fn visit_u64<__E>(self, __value: u64) -> _serde::__private::Result<Self::Value, __E>
|
fn visit_u64<__E>(self, __value: u64) -> _serde::__private::Result<Self::Value, __E>
|
||||||
where
|
where
|
||||||
__E: _serde::de::Error,
|
__E: _serde::de::Error,
|
||||||
{
|
{
|
||||||
match __value {
|
match __value {
|
||||||
#(
|
#(#u64_mapping,)*
|
||||||
#variant_indices => _serde::__private::Ok(#main_constructors),
|
|
||||||
)*
|
|
||||||
_ => #u64_fallthrough_arm,
|
_ => #u64_fallthrough_arm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2394,6 +2387,8 @@ fn deserialize_identifier(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let visit_borrowed = if fallthrough_borrowed.is_some() || collect_other_fields {
|
let visit_borrowed = if fallthrough_borrowed.is_some() || collect_other_fields {
|
||||||
|
let str_mapping = str_mapping.clone();
|
||||||
|
let bytes_mapping = bytes_mapping.clone();
|
||||||
let fallthrough_borrowed_arm = fallthrough_borrowed.as_ref().unwrap_or(fallthrough_arm);
|
let fallthrough_borrowed_arm = fallthrough_borrowed.as_ref().unwrap_or(fallthrough_arm);
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::__private::Result<Self::Value, __E>
|
fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::__private::Result<Self::Value, __E>
|
||||||
@ -2401,9 +2396,7 @@ fn deserialize_identifier(
|
|||||||
__E: _serde::de::Error,
|
__E: _serde::de::Error,
|
||||||
{
|
{
|
||||||
match __value {
|
match __value {
|
||||||
#(
|
#(#str_mapping,)*
|
||||||
#field_strs => _serde::__private::Ok(#constructors),
|
|
||||||
)*
|
|
||||||
_ => {
|
_ => {
|
||||||
#value_as_borrowed_str_content
|
#value_as_borrowed_str_content
|
||||||
#fallthrough_borrowed_arm
|
#fallthrough_borrowed_arm
|
||||||
@ -2416,9 +2409,7 @@ fn deserialize_identifier(
|
|||||||
__E: _serde::de::Error,
|
__E: _serde::de::Error,
|
||||||
{
|
{
|
||||||
match __value {
|
match __value {
|
||||||
#(
|
#(#bytes_mapping,)*
|
||||||
#field_bytes => _serde::__private::Ok(#constructors),
|
|
||||||
)*
|
|
||||||
_ => {
|
_ => {
|
||||||
#bytes_to_str
|
#bytes_to_str
|
||||||
#value_as_borrowed_bytes_content
|
#value_as_borrowed_bytes_content
|
||||||
@ -2443,9 +2434,7 @@ fn deserialize_identifier(
|
|||||||
__E: _serde::de::Error,
|
__E: _serde::de::Error,
|
||||||
{
|
{
|
||||||
match __value {
|
match __value {
|
||||||
#(
|
#(#str_mapping,)*
|
||||||
#field_strs => _serde::__private::Ok(#constructors),
|
|
||||||
)*
|
|
||||||
_ => {
|
_ => {
|
||||||
#value_as_str_content
|
#value_as_str_content
|
||||||
#fallthrough_arm
|
#fallthrough_arm
|
||||||
@ -2458,9 +2447,7 @@ fn deserialize_identifier(
|
|||||||
__E: _serde::de::Error,
|
__E: _serde::de::Error,
|
||||||
{
|
{
|
||||||
match __value {
|
match __value {
|
||||||
#(
|
#(#bytes_mapping,)*
|
||||||
#field_bytes => _serde::__private::Ok(#constructors),
|
|
||||||
)*
|
|
||||||
_ => {
|
_ => {
|
||||||
#bytes_to_str
|
#bytes_to_str
|
||||||
#value_as_bytes_content
|
#value_as_bytes_content
|
||||||
|
@ -183,12 +183,20 @@ impl Name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_aliases(&self) -> Vec<String> {
|
fn deserialize_aliases(&self) -> Vec<String> {
|
||||||
let mut aliases = self.deserialize_aliases.clone();
|
self.deserialize_aliases.clone()
|
||||||
let main_name = self.deserialize_name();
|
}
|
||||||
if !aliases.contains(&main_name) {
|
|
||||||
aliases.push(main_name);
|
fn correct_aliases(&mut self) {
|
||||||
|
// `deserialize_aliases` got from a BTreeSet, so it sorted and does not
|
||||||
|
// contain duplicates.
|
||||||
|
// We cannot insert main name in `new` because rename_all rules not yet
|
||||||
|
// applied there.
|
||||||
|
match self.deserialize_aliases.binary_search(&self.deserialize) {
|
||||||
|
Ok(_) => {} // element already here
|
||||||
|
Err(pos) => self
|
||||||
|
.deserialize_aliases
|
||||||
|
.insert(pos, self.deserialize.clone()),
|
||||||
}
|
}
|
||||||
aliases
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -988,6 +996,7 @@ impl Variant {
|
|||||||
if !self.name.deserialize_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);
|
||||||
}
|
}
|
||||||
|
self.name.correct_aliases();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename_all_rules(&self) -> RenameAllRules {
|
pub fn rename_all_rules(&self) -> RenameAllRules {
|
||||||
@ -1327,6 +1336,7 @@ impl Field {
|
|||||||
if !self.name.deserialize_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);
|
||||||
}
|
}
|
||||||
|
self.name.correct_aliases();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_serializing(&self) -> bool {
|
pub fn skip_serializing(&self) -> bool {
|
||||||
|
@ -605,7 +605,7 @@ fn test_unknown_field_rename_struct() {
|
|||||||
Token::Str("a4"),
|
Token::Str("a4"),
|
||||||
Token::I32(3),
|
Token::I32(3),
|
||||||
],
|
],
|
||||||
"unknown field `a4`, expected one of `a1`, `a3`, `a2`, `a5`, `a6`",
|
"unknown field `a4`, expected one of `a1`, `a2`, `a3`, `a5`, `a6`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,7 +799,7 @@ fn test_unknown_field_rename_enum() {
|
|||||||
Token::Str("d"),
|
Token::Str("d"),
|
||||||
Token::I8(2),
|
Token::I8(2),
|
||||||
],
|
],
|
||||||
"unknown field `d`, expected one of `a`, `c`, `b`, `e`, `f`",
|
"unknown field `d`, expected one of `a`, `b`, `c`, `e`, `f`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,17 +3,21 @@
|
|||||||
#![allow(clippy::derive_partial_eq_without_eq)]
|
#![allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
use serde_test::{assert_de_tokens, Token};
|
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
|
||||||
|
|
||||||
|
mod variant_identifier {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_variant_identifier() {
|
|
||||||
#[derive(Deserialize, Debug, PartialEq)]
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
#[serde(variant_identifier)]
|
#[serde(variant_identifier)]
|
||||||
enum V {
|
enum V {
|
||||||
Aaa,
|
Aaa,
|
||||||
|
#[serde(alias = "Ccc", alias = "Ddd")]
|
||||||
Bbb,
|
Bbb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variant1() {
|
||||||
assert_de_tokens(&V::Aaa, &[Token::U8(0)]);
|
assert_de_tokens(&V::Aaa, &[Token::U8(0)]);
|
||||||
assert_de_tokens(&V::Aaa, &[Token::U16(0)]);
|
assert_de_tokens(&V::Aaa, &[Token::U16(0)]);
|
||||||
assert_de_tokens(&V::Aaa, &[Token::U32(0)]);
|
assert_de_tokens(&V::Aaa, &[Token::U32(0)]);
|
||||||
@ -23,14 +27,64 @@ fn test_variant_identifier() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_field_identifier() {
|
fn aliases() {
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::U8(1)]);
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::U16(1)]);
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::U32(1)]);
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::U64(1)]);
|
||||||
|
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::Str("Bbb")]);
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::Bytes(b"Bbb")]);
|
||||||
|
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::Str("Ccc")]);
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::Bytes(b"Ccc")]);
|
||||||
|
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::Str("Ddd")]);
|
||||||
|
assert_de_tokens(&V::Bbb, &[Token::Bytes(b"Ddd")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unknown() {
|
||||||
|
assert_de_tokens_error::<V>(
|
||||||
|
&[Token::U8(42)],
|
||||||
|
"invalid value: integer `42`, expected variant index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<V>(
|
||||||
|
&[Token::U16(42)],
|
||||||
|
"invalid value: integer `42`, expected variant index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<V>(
|
||||||
|
&[Token::U32(42)],
|
||||||
|
"invalid value: integer `42`, expected variant index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<V>(
|
||||||
|
&[Token::U64(42)],
|
||||||
|
"invalid value: integer `42`, expected variant index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<V>(
|
||||||
|
&[Token::Str("Unknown")],
|
||||||
|
"unknown variant `Unknown`, expected one of `Aaa`, `Bbb`, `Ccc`, `Ddd`",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<V>(
|
||||||
|
&[Token::Bytes(b"Unknown")],
|
||||||
|
"unknown variant `Unknown`, expected one of `Aaa`, `Bbb`, `Ccc`, `Ddd`",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod field_identifier {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, PartialEq)]
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
#[serde(field_identifier, rename_all = "snake_case")]
|
#[serde(field_identifier, rename_all = "snake_case")]
|
||||||
enum F {
|
enum F {
|
||||||
Aaa,
|
Aaa,
|
||||||
|
#[serde(alias = "ccc", alias = "ddd")]
|
||||||
Bbb,
|
Bbb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn field1() {
|
||||||
assert_de_tokens(&F::Aaa, &[Token::U8(0)]);
|
assert_de_tokens(&F::Aaa, &[Token::U8(0)]);
|
||||||
assert_de_tokens(&F::Aaa, &[Token::U16(0)]);
|
assert_de_tokens(&F::Aaa, &[Token::U16(0)]);
|
||||||
assert_de_tokens(&F::Aaa, &[Token::U32(0)]);
|
assert_de_tokens(&F::Aaa, &[Token::U32(0)]);
|
||||||
@ -40,7 +94,52 @@ fn test_field_identifier() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unit_fallthrough() {
|
fn aliases() {
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::U8(1)]);
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::U16(1)]);
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::U32(1)]);
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::U64(1)]);
|
||||||
|
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::Str("bbb")]);
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::Bytes(b"bbb")]);
|
||||||
|
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::Str("ccc")]);
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::Bytes(b"ccc")]);
|
||||||
|
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::Str("ddd")]);
|
||||||
|
assert_de_tokens(&F::Bbb, &[Token::Bytes(b"ddd")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unknown() {
|
||||||
|
assert_de_tokens_error::<F>(
|
||||||
|
&[Token::U8(42)],
|
||||||
|
"invalid value: integer `42`, expected field index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<F>(
|
||||||
|
&[Token::U16(42)],
|
||||||
|
"invalid value: integer `42`, expected field index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<F>(
|
||||||
|
&[Token::U32(42)],
|
||||||
|
"invalid value: integer `42`, expected field index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<F>(
|
||||||
|
&[Token::U64(42)],
|
||||||
|
"invalid value: integer `42`, expected field index 0 <= i < 2",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<F>(
|
||||||
|
&[Token::Str("unknown")],
|
||||||
|
"unknown field `unknown`, expected one of `aaa`, `bbb`, `ccc`, `ddd`",
|
||||||
|
);
|
||||||
|
assert_de_tokens_error::<F>(
|
||||||
|
&[Token::Bytes(b"unknown")],
|
||||||
|
"unknown field `unknown`, expected one of `aaa`, `bbb`, `ccc`, `ddd`",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unit_fallthrough() {
|
||||||
#[derive(Deserialize, Debug, PartialEq)]
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
#[serde(field_identifier, rename_all = "snake_case")]
|
#[serde(field_identifier, rename_all = "snake_case")]
|
||||||
enum F {
|
enum F {
|
||||||
@ -58,7 +157,7 @@ fn test_unit_fallthrough() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_newtype_fallthrough() {
|
fn newtype_fallthrough() {
|
||||||
#[derive(Deserialize, Debug, PartialEq)]
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
#[serde(field_identifier, rename_all = "snake_case")]
|
#[serde(field_identifier, rename_all = "snake_case")]
|
||||||
enum F {
|
enum F {
|
||||||
@ -71,7 +170,7 @@ fn test_newtype_fallthrough() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_newtype_fallthrough_generic() {
|
fn newtype_fallthrough_generic() {
|
||||||
#[derive(Deserialize, Debug, PartialEq)]
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
#[serde(field_identifier, rename_all = "snake_case")]
|
#[serde(field_identifier, rename_all = "snake_case")]
|
||||||
enum F<T> {
|
enum F<T> {
|
||||||
@ -86,3 +185,4 @@ fn test_newtype_fallthrough_generic() {
|
|||||||
assert_de_tokens(&F::Other(42u64), &[Token::U64(42)]);
|
assert_de_tokens(&F::Other(42u64), &[Token::U64(42)]);
|
||||||
assert_de_tokens(&F::Other("x".to_owned()), &[Token::Str("x")]);
|
assert_de_tokens(&F::Other("x".to_owned()), &[Token::Str("x")]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user