Skip to content

Commit

Permalink
Collect errors when deserializing untagged enums
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexTMjugador authored and verorra committed Sep 5, 2024
1 parent 3aca38d commit 813b691
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
32 changes: 26 additions & 6 deletions serde_derive/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1741,12 +1741,24 @@ fn deserialize_untagged_enum_after(
.iter()
.filter(|variant| !variant.attrs.skip_deserializing())
.map(|variant| {
Expr(deserialize_untagged_variant(
let de_any = Expr(deserialize_untagged_variant(
params,
variant,
cattrs,
quote!(__deserializer),
))
));

let variant_name = variant.ident.to_string();
quote! {
_serde::__private::Result::map_err(
#de_any,
|e| -> __D::Error {
_serde::de::Error::custom(
::std::format!("attempted to deserialize `{}` but failed with: {}", #variant_name, ::std::string::ToString::to_string(&e))
)
},
)
}
});
// TODO this message could be better by saving the errors from the failed
// attempts. The heuristic used by TOML was to count the number of fields
Expand All @@ -1755,10 +1767,11 @@ fn deserialize_untagged_enum_after(
// better to save all the errors and combine them into one message that
// explains why none of the variants matched.
let fallthrough_msg = format!(
"data did not match any variant of untagged enum {}",
"data did not match any variant of untagged enum `{}`",
params.type_name()
);
let fallthrough_msg = cattrs.expecting().unwrap_or(&fallthrough_msg);
let has_custom_err_msg = cattrs.expecting().is_some();

// Ignore any error associated with non-untagged deserialization so that we
// can fall through to the untagged variants. This may be infallible so we
Expand All @@ -1774,16 +1787,23 @@ fn deserialize_untagged_enum_after(
quote_block! {
let __content = <_serde::__private::de::Content as _serde::Deserialize>::deserialize(__deserializer)?;
let __deserializer = _serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content);
let mut fallthrough_msg = ::std::string::ToString::to_string(#fallthrough_msg);

#first_attempt

#(
if let _serde::__private::Ok(__ok) = #attempts {
return _serde::__private::Ok(__ok);
match #attempts {
_serde::__private::Ok(__ok) => return _serde::__private::Ok(__ok),
_serde::__private::Err(__err) => {
if !#has_custom_err_msg {
fallthrough_msg.push_str("\n\t- ");
fallthrough_msg.push_str(&::std::string::ToString::to_string(&__err));
}
}
}
)*

_serde::__private::Err(_serde::de::Error::custom(#fallthrough_msg))
_serde::__private::Err(_serde::de::Error::custom(fallthrough_msg))
}
}

Expand Down
16 changes: 14 additions & 2 deletions test_suite/tests/test_enum_untagged.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,13 @@ fn complex() {

assert_de_tokens_error::<Untagged>(
&[Token::Tuple { len: 1 }, Token::U8(1), Token::TupleEnd],
"data did not match any variant of untagged enum Untagged",
"data did not match any variant of untagged enum `Untagged`
\t- attempted to deserialize `A` but failed with: invalid type: sequence, expected struct variant Untagged::A
\t- attempted to deserialize `B` but failed with: invalid type: sequence, expected struct variant Untagged::B
\t- attempted to deserialize `C` but failed with: invalid type: sequence, expected unit variant Untagged::C
\t- attempted to deserialize `D` but failed with: invalid type: sequence, expected u8
\t- attempted to deserialize `E` but failed with: invalid type: sequence, expected a string
\t- attempted to deserialize `F` but failed with: invalid length 1, expected tuple variant Untagged::F with 2 elements"
);

assert_de_tokens_error::<Untagged>(
Expand All @@ -82,7 +88,13 @@ fn complex() {
Token::U8(3),
Token::TupleEnd,
],
"data did not match any variant of untagged enum Untagged",
"data did not match any variant of untagged enum `Untagged`
\t- attempted to deserialize `A` but failed with: invalid type: sequence, expected struct variant Untagged::A
\t- attempted to deserialize `B` but failed with: invalid type: sequence, expected struct variant Untagged::B
\t- attempted to deserialize `C` but failed with: invalid type: sequence, expected unit variant Untagged::C
\t- attempted to deserialize `D` but failed with: invalid type: sequence, expected u8
\t- attempted to deserialize `E` but failed with: invalid type: sequence, expected a string
\t- attempted to deserialize `F` but failed with: invalid length 3, expected 2 elements in sequence"
);
}

Expand Down

0 comments on commit 813b691

Please sign in to comment.