Merge pull request 1916 from Mingun/expecting-customize

This commit is contained in:
David Tolnay
2021-01-23 13:38:03 -08:00
6 changed files with 238 additions and 8 deletions
+4 -2
View File
@@ -825,15 +825,17 @@ mod content {
/// Not public API. /// Not public API.
pub struct TaggedContentVisitor<'de, T> { pub struct TaggedContentVisitor<'de, T> {
tag_name: &'static str, tag_name: &'static str,
expecting: &'static str,
value: PhantomData<TaggedContent<'de, T>>, value: PhantomData<TaggedContent<'de, T>>,
} }
impl<'de, T> TaggedContentVisitor<'de, T> { impl<'de, T> TaggedContentVisitor<'de, T> {
/// Visitor for the content of an internally tagged enum with the given /// Visitor for the content of an internally tagged enum with the given
/// tag name. /// tag name.
pub fn new(name: &'static str) -> Self { pub fn new(name: &'static str, expecting: &'static str) -> Self {
TaggedContentVisitor { TaggedContentVisitor {
tag_name: name, tag_name: name,
expecting: expecting,
value: PhantomData, value: PhantomData,
} }
} }
@@ -862,7 +864,7 @@ mod content {
type Value = TaggedContent<'de, T>; type Value = TaggedContent<'de, T>;
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str("internally tagged enum") fmt.write_str(self.expecting)
} }
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error> fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
+19 -3
View File
@@ -402,6 +402,7 @@ fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fra
let type_name = cattrs.name().deserialize_name(); let type_name = cattrs.name().deserialize_name();
let expecting = format!("unit struct {}", params.type_name()); let expecting = format!("unit struct {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);
quote_block! { quote_block! {
struct __Visitor; struct __Visitor;
@@ -459,6 +460,7 @@ fn deserialize_tuple(
Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident), Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
None => format!("tuple struct {}", params.type_name()), None => format!("tuple struct {}", params.type_name()),
}; };
let expecting = cattrs.expecting().unwrap_or(&expecting);
let nfields = fields.len(); let nfields = fields.len();
@@ -545,6 +547,7 @@ fn deserialize_tuple_in_place(
Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident), Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
None => format!("tuple struct {}", params.type_name()), None => format!("tuple struct {}", params.type_name()),
}; };
let expecting = cattrs.expecting().unwrap_or(&expecting);
let nfields = fields.len(); let nfields = fields.len();
@@ -633,6 +636,7 @@ fn deserialize_seq(
} else { } else {
format!("{} with {} elements", expecting, deserialized_count) format!("{} with {} elements", expecting, deserialized_count)
}; };
let expecting = cattrs.expecting().unwrap_or(&expecting);
let mut index_in_seq = 0_usize; let mut index_in_seq = 0_usize;
let let_values = vars.clone().zip(fields).map(|(var, field)| { let let_values = vars.clone().zip(fields).map(|(var, field)| {
@@ -735,6 +739,7 @@ fn deserialize_seq_in_place(
} else { } else {
format!("{} with {} elements", expecting, deserialized_count) format!("{} with {} elements", expecting, deserialized_count)
}; };
let expecting = cattrs.expecting().unwrap_or(&expecting);
let mut index_in_seq = 0usize; let mut index_in_seq = 0usize;
let write_values = fields.iter().map(|field| { let write_values = fields.iter().map(|field| {
@@ -910,6 +915,7 @@ fn deserialize_struct(
Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident), Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
None => format!("struct {}", params.type_name()), None => format!("struct {}", params.type_name()),
}; };
let expecting = cattrs.expecting().unwrap_or(&expecting);
let visit_seq = Stmts(deserialize_seq( let visit_seq = Stmts(deserialize_seq(
&type_path, params, fields, true, cattrs, &expecting, &type_path, params, fields, true, cattrs, &expecting,
@@ -1051,6 +1057,7 @@ fn deserialize_struct_in_place(
Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident), Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
None => format!("struct {}", params.type_name()), None => format!("struct {}", params.type_name()),
}; };
let expecting = cattrs.expecting().unwrap_or(&expecting);
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting)); let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
@@ -1203,6 +1210,7 @@ fn deserialize_externally_tagged_enum(
let type_name = cattrs.name().deserialize_name(); let type_name = cattrs.name().deserialize_name();
let expecting = format!("enum {}", params.type_name()); let expecting = format!("enum {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs); let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
@@ -1312,6 +1320,9 @@ fn deserialize_internally_tagged_enum(
} }
}); });
let expecting = format!("internally tagged enum {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);
quote_block! { quote_block! {
#variant_visitor #variant_visitor
@@ -1319,7 +1330,7 @@ fn deserialize_internally_tagged_enum(
let __tagged = try!(_serde::Deserializer::deserialize_any( let __tagged = try!(_serde::Deserializer::deserialize_any(
__deserializer, __deserializer,
_serde::__private::de::TaggedContentVisitor::<__Field>::new(#tag))); _serde::__private::de::TaggedContentVisitor::<__Field>::new(#tag, #expecting)));
match __tagged.tag { match __tagged.tag {
#(#variant_arms)* #(#variant_arms)*
@@ -1362,6 +1373,7 @@ fn deserialize_adjacently_tagged_enum(
.collect(); .collect();
let expecting = format!("adjacently tagged enum {}", params.type_name()); let expecting = format!("adjacently tagged enum {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);
let type_name = cattrs.name().deserialize_name(); let type_name = cattrs.name().deserialize_name();
let deny_unknown_fields = cattrs.deny_unknown_fields(); let deny_unknown_fields = cattrs.deny_unknown_fields();
@@ -1653,6 +1665,7 @@ fn deserialize_untagged_enum(
"data did not match any variant of untagged enum {}", "data did not match any variant of untagged enum {}",
params.type_name() params.type_name()
); );
let fallthrough_msg = cattrs.expecting().unwrap_or(&fallthrough_msg);
quote_block! { quote_block! {
let __content = try!(<_serde::__private::de::Content as _serde::Deserialize>::deserialize(__deserializer)); let __content = try!(<_serde::__private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
@@ -1916,6 +1929,7 @@ fn deserialize_generated_identifier(
is_variant, is_variant,
fallthrough, fallthrough,
!is_variant && cattrs.has_flatten(), !is_variant && cattrs.has_flatten(),
None,
)); ));
let lifetime = if !is_variant && cattrs.has_flatten() { let lifetime = if !is_variant && cattrs.has_flatten() {
@@ -2029,6 +2043,7 @@ fn deserialize_custom_identifier(
is_variant, is_variant,
fallthrough, fallthrough,
false, false,
cattrs.expecting(),
)); ));
quote_block! { quote_block! {
@@ -2060,6 +2075,7 @@ fn deserialize_identifier(
// .0 for referenced data, .1 -- for borrowed // .0 for referenced data, .1 -- for borrowed
fallthrough: Option<(TokenStream, TokenStream)>, fallthrough: Option<(TokenStream, TokenStream)>,
collect_other_fields: bool, collect_other_fields: bool,
expecting: Option<&str>,
) -> Fragment { ) -> Fragment {
let mut flat_fields = Vec::new(); let mut flat_fields = Vec::new();
for (_, ident, aliases) in fields { for (_, ident, aliases) in fields {
@@ -2081,11 +2097,11 @@ fn deserialize_identifier(
.map(|(_, ident, _)| quote!(#this::#ident)) .map(|(_, ident, _)| quote!(#this::#ident))
.collect(); .collect();
let expecting = if is_variant { let expecting = expecting.unwrap_or(if is_variant {
"variant identifier" "variant identifier"
} else { } else {
"field identifier" "field identifier"
}; });
let index_expecting = if is_variant { "variant" } else { "field" }; let index_expecting = if is_variant { "variant" } else { "field" };
+17
View File
@@ -223,6 +223,8 @@ pub struct Container {
has_flatten: bool, has_flatten: bool,
serde_path: Option<syn::Path>, serde_path: Option<syn::Path>,
is_packed: bool, is_packed: bool,
/// Error message generated when type can't be deserialized
expecting: Option<String>,
} }
/// Styles of representing an enum. /// Styles of representing an enum.
@@ -305,6 +307,7 @@ impl Container {
let mut field_identifier = BoolAttr::none(cx, FIELD_IDENTIFIER); let mut field_identifier = BoolAttr::none(cx, FIELD_IDENTIFIER);
let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER); let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER);
let mut serde_path = Attr::none(cx, CRATE); let mut serde_path = Attr::none(cx, CRATE);
let mut expecting = Attr::none(cx, EXPECTING);
for meta_item in item for meta_item in item
.attrs .attrs
@@ -575,6 +578,13 @@ impl Container {
} }
} }
// Parse `#[serde(expecting = "a message")]`
Meta(NameValue(m)) if m.path == EXPECTING => {
if let Ok(s) = get_lit_str(cx, EXPECTING, &m.lit) {
expecting.set(&m.path, s.value());
}
}
Meta(meta_item) => { Meta(meta_item) => {
let path = meta_item let path = meta_item
.path() .path()
@@ -627,6 +637,7 @@ impl Container {
has_flatten: false, has_flatten: false,
serde_path: serde_path.get(), serde_path: serde_path.get(),
is_packed, is_packed,
expecting: expecting.get(),
} }
} }
@@ -702,6 +713,12 @@ impl Container {
self.custom_serde_path() self.custom_serde_path()
.map_or_else(|| Cow::Owned(parse_quote!(_serde)), Cow::Borrowed) .map_or_else(|| Cow::Owned(parse_quote!(_serde)), Cow::Borrowed)
} }
/// Error message generated when type can't be deserialized.
/// If `None`, default message will be used
pub fn expecting(&self) -> Option<&str> {
self.expecting.as_ref().map(String::as_ref)
}
} }
fn decide_tag( fn decide_tag(
+1
View File
@@ -35,6 +35,7 @@ pub const TRY_FROM: Symbol = Symbol("try_from");
pub const UNTAGGED: Symbol = Symbol("untagged"); pub const UNTAGGED: Symbol = Symbol("untagged");
pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier"); pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
pub const WITH: Symbol = Symbol("with"); pub const WITH: Symbol = Symbol("with");
pub const EXPECTING: Symbol = Symbol("expecting");
impl PartialEq<Symbol> for Ident { impl PartialEq<Symbol> for Ident {
fn eq(&self, word: &Symbol) -> bool { fn eq(&self, word: &Symbol) -> bool {
+1 -3
View File
@@ -250,9 +250,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
{ {
visitor.visit_enum(DeserializerEnumVisitor { de: self }) visitor.visit_enum(DeserializerEnumVisitor { de: self })
} }
_ => { _ => self.deserialize_any(visitor)
unexpected!(self.next_token());
}
} }
} }
+196
View File
@@ -2637,3 +2637,199 @@ fn test_flatten_any_after_flatten_struct() {
], ],
); );
} }
#[test]
fn test_expecting_message() {
#[derive(Deserialize, PartialEq, Debug)]
#[serde(expecting = "something strange...")]
struct Unit;
#[derive(Deserialize)]
#[serde(expecting = "something strange...")]
struct Newtype(bool);
#[derive(Deserialize)]
#[serde(expecting = "something strange...")]
struct Tuple(u32, bool);
#[derive(Deserialize)]
#[serde(expecting = "something strange...")]
struct Struct {
question: String,
answer: u32,
}
assert_de_tokens_error::<Unit>(
&[Token::Str("Unit")],
r#"invalid type: string "Unit", expected something strange..."#
);
assert_de_tokens_error::<Newtype>(
&[Token::Str("Newtype")],
r#"invalid type: string "Newtype", expected something strange..."#
);
assert_de_tokens_error::<Tuple>(
&[Token::Str("Tuple")],
r#"invalid type: string "Tuple", expected something strange..."#
);
assert_de_tokens_error::<Struct>(
&[Token::Str("Struct")],
r#"invalid type: string "Struct", expected something strange..."#
);
}
#[test]
fn test_expecting_message_externally_tagged_enum() {
#[derive(Deserialize)]
#[serde(expecting = "something strange...")]
enum Enum {
ExternallyTagged,
}
assert_de_tokens_error::<Enum>(
&[
Token::Str("ExternallyTagged"),
],
r#"invalid type: string "ExternallyTagged", expected something strange..."#
);
// Check that #[serde(expecting = "...")] doesn't affect variant identifier error message
assert_de_tokens_error::<Enum>(
&[
Token::Enum { name: "Enum" },
Token::Unit,
],
r#"invalid type: unit value, expected variant identifier"#
);
}
#[test]
fn test_expecting_message_internally_tagged_enum() {
#[derive(Deserialize)]
#[serde(tag = "tag")]
#[serde(expecting = "something strange...")]
enum Enum {
InternallyTagged,
}
assert_de_tokens_error::<Enum>(
&[
Token::Str("InternallyTagged"),
],
r#"invalid type: string "InternallyTagged", expected something strange..."#
);
// Check that #[serde(expecting = "...")] doesn't affect variant identifier error message
assert_de_tokens_error::<Enum>(
&[
Token::Map { len: None },
Token::Str("tag"),
Token::Unit,
],
r#"invalid type: unit value, expected variant identifier"#
);
}
#[test]
fn test_expecting_message_adjacently_tagged_enum() {
#[derive(Deserialize)]
#[serde(tag = "tag", content = "content")]
#[serde(expecting = "something strange...")]
enum Enum {
AdjacentlyTagged,
}
assert_de_tokens_error::<Enum>(
&[
Token::Str("AdjacentlyTagged"),
],
r#"invalid type: string "AdjacentlyTagged", expected something strange..."#
);
assert_de_tokens_error::<Enum>(
&[
Token::Map { len: None },
Token::Unit,
],
r#"invalid type: unit value, expected "tag", "content", or other ignored fields"#
);
// Check that #[serde(expecting = "...")] doesn't affect variant identifier error message
assert_de_tokens_error::<Enum>(
&[
Token::Map { len: None },
Token::Str("tag"),
Token::Unit,
],
r#"invalid type: unit value, expected variant identifier"#
);
}
#[test]
fn test_expecting_message_untagged_tagged_enum() {
#[derive(Deserialize)]
#[serde(untagged)]
#[serde(expecting = "something strange...")]
enum Enum {
Untagged,
}
assert_de_tokens_error::<Enum>(
&[
Token::Str("Untagged"),
],
r#"something strange..."#
);
}
#[test]
fn test_expecting_message_identifier_enum() {
#[derive(Deserialize)]
#[serde(field_identifier)]
#[serde(expecting = "something strange...")]
enum FieldEnum {
Field,
}
#[derive(Deserialize)]
#[serde(variant_identifier)]
#[serde(expecting = "something strange...")]
enum VariantEnum {
Variant,
}
assert_de_tokens_error::<FieldEnum>(
&[
Token::Unit,
],
r#"invalid type: unit value, expected something strange..."#
);
assert_de_tokens_error::<FieldEnum>(
&[
Token::Enum { name: "FieldEnum" },
Token::Str("Unknown"),
Token::None,
],
r#"invalid type: map, expected something strange..."#
);
assert_de_tokens_error::<VariantEnum>(
&[
Token::Unit,
],
r#"invalid type: unit value, expected something strange..."#
);
assert_de_tokens_error::<VariantEnum>(
&[
Token::Enum { name: "VariantEnum" },
Token::Str("Unknown"),
Token::None,
],
r#"invalid type: map, expected something strange..."#
);
}