Skip to content

Commit 35a042b

Browse files
author
Pascal Hertleif
committed
Collect errors when deserializing untagged enums
This also adds a verbose-debug feature that enables the new behavior. Previously: > data did not match any variant of untagged enum ReferenceOr at line 6 column 4 Now, with `verbose-debug` feature enabled: > data did not match any variant of untagged enum `ReferenceOr` > - attempted to deserialize `Reference` but failed with: missing field `$ref` > - attempted to deserialize `Item` but failed with: invalid value: string "lol", expected expected format `\dXX` at line 6 column 4 cf. #773
1 parent b54821d commit 35a042b

File tree

3 files changed

+58
-20
lines changed

3 files changed

+58
-20
lines changed

serde/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,6 @@ alloc = ["unstable"]
5858
# does not preserve identity and may result in multiple copies of the same data.
5959
# Be sure that this is what you want before enabling this feature.
6060
rc = []
61+
62+
# Opt into verbose errors that are more expensive to compute.
63+
verbose-debug = ["serde_derive/verbose-debug"]

serde_derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ appveyor = { repository = "serde-rs/serde" }
1818
[features]
1919
default = []
2020
deserialize_in_place = []
21+
verbose-debug = []
2122

2223
[lib]
2324
name = "serde_derive"

serde_derive/src/de.rs

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,35 +1621,69 @@ fn deserialize_untagged_enum(
16211621
.iter()
16221622
.filter(|variant| !variant.attrs.skip_deserializing())
16231623
.map(|variant| {
1624-
Expr(deserialize_untagged_variant(
1624+
let de_any = Expr(deserialize_untagged_variant(
16251625
params,
16261626
variant,
16271627
cattrs,
16281628
quote!(_serde::private::de::ContentRefDeserializer::<__D::Error>::new(&__content)),
1629-
))
1629+
));
1630+
1631+
if cfg!(feature = "verbose-debug") {
1632+
let variant_name = variant.ident.to_string();
1633+
quote! {
1634+
_serde::export::Result::map_err(
1635+
#de_any,
1636+
|e| -> __D::Error {
1637+
_serde::de::Error::custom(
1638+
format!("attempted to deserialize `{}` but failed with: {}", #variant_name, e.to_string())
1639+
)
1640+
},
1641+
)
1642+
}
1643+
} else {
1644+
quote! { #de_any }
1645+
}
16301646
});
16311647

1632-
// TODO this message could be better by saving the errors from the failed
1633-
// attempts. The heuristic used by TOML was to count the number of fields
1634-
// processed before an error, and use the error that happened after the
1635-
// largest number of fields. I'm not sure I like that. Maybe it would be
1636-
// better to save all the errors and combine them into one message that
1637-
// explains why none of the variants matched.
1638-
let fallthrough_msg = format!(
1639-
"data did not match any variant of untagged enum {}",
1640-
params.type_name()
1641-
);
1648+
if cfg!(feature = "verbose-debug") {
1649+
let type_name = params.type_name();
16421650

1643-
quote_block! {
1644-
let __content = try!(<_serde::private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
1651+
quote_block! {
1652+
let __content = try!(<_serde::private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
1653+
let mut fallthrough_msg = format!(
1654+
"data did not match any variant of untagged enum `{}`",
1655+
#type_name
1656+
);
16451657

1646-
#(
1647-
if let _serde::export::Ok(__ok) = #attempts {
1648-
return _serde::export::Ok(__ok);
1649-
}
1650-
)*
1658+
#(
1659+
match #attempts {
1660+
_serde::export::Ok(__ok) => return _serde::export::Ok(__ok),
1661+
_serde::export::Err(__err) => {
1662+
fallthrough_msg.push_str("\n\t- ");
1663+
fallthrough_msg.push_str(&__err.to_string());
1664+
}
1665+
}
1666+
)*
1667+
1668+
_serde::export::Err(_serde::de::Error::custom(fallthrough_msg))
1669+
}
1670+
} else {
1671+
let fallthrough_msg = format!(
1672+
"data did not match any variant of untagged enum {}",
1673+
params.type_name()
1674+
);
16511675

1652-
_serde::export::Err(_serde::de::Error::custom(#fallthrough_msg))
1676+
quote_block! {
1677+
let __content = try!(<_serde::private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
1678+
1679+
#(
1680+
if let _serde::export::Ok(__ok) = #attempts {
1681+
return _serde::export::Ok(__ok);
1682+
}
1683+
)*
1684+
1685+
_serde::export::Err(_serde::de::Error::custom(#fallthrough_msg))
1686+
}
16531687
}
16541688
}
16551689

0 commit comments

Comments
 (0)