Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new container attribute #[serde(expecting = "...")] for specifing custom expectation message #1916

Merged
merged 2 commits into from Jan 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions serde/src/private/de.rs
Expand Up @@ -824,15 +824,17 @@ mod content {
/// Not public API.
pub struct TaggedContentVisitor<'de, T> {
tag_name: &'static str,
expecting: &'static str,
value: PhantomData<TaggedContent<'de, T>>,
}

impl<'de, T> TaggedContentVisitor<'de, T> {
/// Visitor for the content of an internally tagged enum with the given
/// tag name.
pub fn new(name: &'static str) -> Self {
pub fn new(name: &'static str, expecting: &'static str) -> Self {
TaggedContentVisitor {
tag_name: name,
expecting: expecting,
value: PhantomData,
}
}
Expand Down Expand Up @@ -861,7 +863,7 @@ mod content {
type Value = TaggedContent<'de, T>;

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>
Expand Down
22 changes: 19 additions & 3 deletions serde_derive/src/de.rs
Expand Up @@ -399,6 +399,7 @@ fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fra
let type_name = cattrs.name().deserialize_name();

let expecting = format!("unit struct {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);

quote_block! {
struct __Visitor;
Expand Down Expand Up @@ -456,6 +457,7 @@ fn deserialize_tuple(
Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
None => format!("tuple struct {}", params.type_name()),
};
let expecting = cattrs.expecting().unwrap_or(&expecting);

let nfields = fields.len();

Expand Down Expand Up @@ -542,6 +544,7 @@ fn deserialize_tuple_in_place(
Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
None => format!("tuple struct {}", params.type_name()),
};
let expecting = cattrs.expecting().unwrap_or(&expecting);

let nfields = fields.len();

Expand Down Expand Up @@ -630,6 +633,7 @@ fn deserialize_seq(
} else {
format!("{} with {} elements", expecting, deserialized_count)
};
let expecting = cattrs.expecting().unwrap_or(&expecting);

let mut index_in_seq = 0_usize;
let let_values = vars.clone().zip(fields).map(|(var, field)| {
Expand Down Expand Up @@ -732,6 +736,7 @@ fn deserialize_seq_in_place(
} else {
format!("{} with {} elements", expecting, deserialized_count)
};
let expecting = cattrs.expecting().unwrap_or(&expecting);

let mut index_in_seq = 0usize;
let write_values = fields.iter().map(|field| {
Expand Down Expand Up @@ -907,6 +912,7 @@ fn deserialize_struct(
Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
None => format!("struct {}", params.type_name()),
};
let expecting = cattrs.expecting().unwrap_or(&expecting);

let visit_seq = Stmts(deserialize_seq(
&type_path, params, fields, true, cattrs, &expecting,
Expand Down Expand Up @@ -1048,6 +1054,7 @@ fn deserialize_struct_in_place(
Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
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));

Expand Down Expand Up @@ -1200,6 +1207,7 @@ fn deserialize_externally_tagged_enum(

let type_name = cattrs.name().deserialize_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);

Expand Down Expand Up @@ -1309,14 +1317,17 @@ fn deserialize_internally_tagged_enum(
}
});

let expecting = format!("internally tagged enum {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);

quote_block! {
#variant_visitor

#variants_stmt

let __tagged = try!(_serde::Deserializer::deserialize_any(
__deserializer,
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag, #expecting)));

match __tagged.tag {
#(#variant_arms)*
Expand Down Expand Up @@ -1359,6 +1370,7 @@ fn deserialize_adjacently_tagged_enum(
.collect();

let expecting = format!("adjacently tagged enum {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);
let type_name = cattrs.name().deserialize_name();
let deny_unknown_fields = cattrs.deny_unknown_fields();

Expand Down Expand Up @@ -1648,6 +1660,7 @@ fn deserialize_untagged_enum(
"data did not match any variant of untagged enum {}",
params.type_name()
);
let fallthrough_msg = cattrs.expecting().unwrap_or(&fallthrough_msg);

quote_block! {
let __content = try!(<_serde::private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
Expand Down Expand Up @@ -1905,6 +1918,7 @@ fn deserialize_generated_identifier(
is_variant,
fallthrough,
!is_variant && cattrs.has_flatten(),
None,
));

let lifetime = if !is_variant && cattrs.has_flatten() {
Expand Down Expand Up @@ -2012,6 +2026,7 @@ fn deserialize_custom_identifier(
is_variant,
fallthrough,
false,
cattrs.expecting(),
));

quote_block! {
Expand Down Expand Up @@ -2042,6 +2057,7 @@ fn deserialize_identifier(
is_variant: bool,
fallthrough: Option<TokenStream>,
collect_other_fields: bool,
expecting: Option<&str>,
) -> Fragment {
let mut flat_fields = Vec::new();
for (_, ident, aliases) in fields {
Expand All @@ -2066,11 +2082,11 @@ fn deserialize_identifier(
.map(|(_, ident, _)| quote!(#this::#ident))
.collect();

let expecting = if is_variant {
let expecting = expecting.unwrap_or(if is_variant {
"variant identifier"
} else {
"field identifier"
};
});

let index_expecting = if is_variant { "variant" } else { "field" };

Expand Down
17 changes: 17 additions & 0 deletions serde_derive/src/internals/attr.rs
Expand Up @@ -223,6 +223,8 @@ pub struct Container {
has_flatten: bool,
serde_path: Option<syn::Path>,
is_packed: bool,
/// Error message generated when type can't be deserialized
expecting: Option<String>,
}

/// Styles of representing an enum.
Expand Down Expand Up @@ -305,6 +307,7 @@ impl Container {
let mut field_identifier = BoolAttr::none(cx, FIELD_IDENTIFIER);
let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER);
let mut serde_path = Attr::none(cx, CRATE);
let mut expecting = Attr::none(cx, EXPECTING);

for meta_item in item
.attrs
Expand Down Expand Up @@ -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) => {
let path = meta_item
.path()
Expand Down Expand Up @@ -627,6 +637,7 @@ impl Container {
has_flatten: false,
serde_path: serde_path.get(),
is_packed,
expecting: expecting.get(),
}
}

Expand Down Expand Up @@ -702,6 +713,12 @@ impl Container {
self.custom_serde_path()
.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(
Expand Down
1 change: 1 addition & 0 deletions serde_derive/src/internals/symbol.rs
Expand Up @@ -35,6 +35,7 @@ pub const TRY_FROM: Symbol = Symbol("try_from");
pub const UNTAGGED: Symbol = Symbol("untagged");
pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
pub const WITH: Symbol = Symbol("with");
pub const EXPECTING: Symbol = Symbol("expecting");

impl PartialEq<Symbol> for Ident {
fn eq(&self, word: &Symbol) -> bool {
Expand Down
4 changes: 1 addition & 3 deletions serde_test/src/de.rs
Expand Up @@ -250,9 +250,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
{
visitor.visit_enum(DeserializerEnumVisitor { de: self })
}
_ => {
unexpected!(self.next_token());
}
_ => self.deserialize_any(visitor)
}
}

Expand Down