Skip to content

Commit

Permalink
Merge pull request #2570 from dtolnay/remotenonexhaustive
Browse files Browse the repository at this point in the history
Treat unmatched non-exhaustive remote variant as serde(skip)
  • Loading branch information
dtolnay authored Aug 14, 2023
2 parents 45271c3 + 8d3a032 commit 5e56c9f
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 1 deletion.
13 changes: 13 additions & 0 deletions serde/src/private/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
9 changes: 9 additions & 0 deletions serde_derive/src/internals/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -587,6 +591,7 @@ impl Container {
serde_path: serde_path.get(),
is_packed,
expecting: expecting.get(),
non_exhaustive,
}
}

Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions serde_derive/src/internals/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
8 changes: 7 additions & 1 deletion serde_derive/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,14 +401,20 @@ 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)| {
serialize_variant(params, variant, variant_index as u32, cattrs)
})
.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)*
Expand Down
12 changes: 12 additions & 0 deletions test_suite/tests/test_remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ struct Test {

#[serde(with = "EnumConcrete")]
enum_concrete: remote::EnumGeneric<u8>,

#[serde(with = "ErrorKindDef")]
io_error_kind: std::io::ErrorKind,
}

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -197,6 +200,15 @@ enum EnumConcrete {
Variant(u8),
}

#[derive(Serialize, Deserialize)]
#[serde(remote = "std::io::ErrorKind")]
#[non_exhaustive]
enum ErrorKindDef {
NotFound,
PermissionDenied,
// ...
}

impl From<PrimitivePrivDef> for remote::PrimitivePriv {
fn from(def: PrimitivePrivDef) -> Self {
remote::PrimitivePriv::new(def.0)
Expand Down

0 comments on commit 5e56c9f

Please sign in to comment.