Treat unmatched non-exhaustive remote variant as serde(skip)

This commit is contained in:
David Tolnay 2023-08-13 21:14:33 -07:00
parent cb490ec16d
commit 8d3a03288b
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
4 changed files with 30 additions and 1 deletions

View File

@ -1370,3 +1370,16 @@ impl Serialize for AdjacentlyTaggedEnumVariant {
serializer.serialize_unit_variant(self.enum_name, self.variant_index, self.variant_name)
}
}
// Error when Serialize for a non_exhaustive remote enum encounters a variant
// that is not recognized.
pub struct CannotSerializeVariant<T>(pub T);
impl<T> Display for CannotSerializeVariant<T>
where
T: Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "enum variant cannot be serialized: {:?}", self.0)
}
}

View File

@ -221,6 +221,7 @@ pub struct Container {
is_packed: bool,
/// Error message generated when type can't be deserialized
expecting: Option<String>,
non_exhaustive: bool,
}
/// Styles of representing an enum.
@ -306,9 +307,12 @@ impl Container {
let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER);
let mut serde_path = Attr::none(cx, CRATE);
let mut expecting = Attr::none(cx, EXPECTING);
let mut non_exhaustive = false;
for attr in &item.attrs {
if attr.path() != SERDE {
non_exhaustive |=
matches!(&attr.meta, syn::Meta::Path(path) if path == NON_EXHAUSTIVE);
continue;
}
@ -587,6 +591,7 @@ impl Container {
serde_path: serde_path.get(),
is_packed,
expecting: expecting.get(),
non_exhaustive,
}
}
@ -672,6 +677,10 @@ impl Container {
pub fn expecting(&self) -> Option<&str> {
self.expecting.as_ref().map(String::as_ref)
}
pub fn non_exhaustive(&self) -> bool {
self.non_exhaustive
}
}
fn decide_tag(

View File

@ -19,6 +19,7 @@ pub const FLATTEN: Symbol = Symbol("flatten");
pub const FROM: Symbol = Symbol("from");
pub const GETTER: Symbol = Symbol("getter");
pub const INTO: Symbol = Symbol("into");
pub const NON_EXHAUSTIVE: Symbol = Symbol("non_exhaustive");
pub const OTHER: Symbol = Symbol("other");
pub const REMOTE: Symbol = Symbol("remote");
pub const RENAME: Symbol = Symbol("rename");

View File

@ -401,7 +401,7 @@ fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Cont
let self_var = &params.self_var;
let arms: Vec<_> = variants
let mut arms: Vec<_> = variants
.iter()
.enumerate()
.map(|(variant_index, variant)| {
@ -409,6 +409,12 @@ fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Cont
})
.collect();
if cattrs.non_exhaustive() {
arms.push(quote! {
unrecognized => _serde::__private::Err(_serde::ser::Error::custom(_serde::__private::ser::CannotSerializeVariant(unrecognized))),
});
}
quote_expr! {
match *#self_var {
#(#arms)*