Skip to content

Commit

Permalink
Support deserializing enum values in internally/untagged enums
Browse files Browse the repository at this point in the history
Specifically, this deserializes the "variant" as a `Content` and visits
the value with `newtype_variant::<Content>`.

There is no perfect solution here as the enum value could be a map,
tuple, unit, etc. However, given that we're trying to deserialize
arbitrary values, deserializing as a "newtype" actually makes a lot of
sense:

1. Deserializers can opt-in to this fairly easily by implementing
   `EnumAccess::newtype_variant_seed`.
2. Types (implementing `Deserialize`) should "just work" as expected
   because maps, tuples, etc. are preserved.
  • Loading branch information
Stebalien committed Feb 8, 2024
1 parent ede9762 commit 3394f7b
Showing 1 changed file with 24 additions and 4 deletions.
28 changes: 24 additions & 4 deletions serde/src/private/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ mod content {
// This issue is tracking making some of this stuff public:
// https://github.com/serde-rs/serde/issues/741

use crate::de::VariantAccess;
use crate::lib::*;

use crate::actually_private;
Expand Down Expand Up @@ -249,6 +250,7 @@ mod content {
Newtype(Box<Content<'de>>),
Seq(Vec<Content<'de>>),
Map(Vec<(Content<'de>, Content<'de>)>),
Enum(Box<(Content<'de>, Content<'de>)>),
}

impl<'de> Content<'de> {
Expand Down Expand Up @@ -286,6 +288,7 @@ mod content {
Content::Newtype(_) => Unexpected::NewtypeStruct,
Content::Seq(_) => Unexpected::Seq,
Content::Map(_) => Unexpected::Map,
Content::Enum(_) => Unexpected::Enum,
}
}
}
Expand Down Expand Up @@ -510,13 +513,13 @@ mod content {
Ok(Content::Map(vec))
}

fn visit_enum<V>(self, _visitor: V) -> Result<Self::Value, V::Error>
fn visit_enum<V>(self, visitor: V) -> Result<Self::Value, V::Error>
where
V: EnumAccess<'de>,
{
Err(de::Error::custom(
"untagged and internally tagged enums do not support enum input",
))
let (variant, access) = tri!(visitor.variant());
let value = tri!(access.newtype_variant());
Ok(Content::Enum(Box::new((variant, value))))
}
}

Expand Down Expand Up @@ -1146,6 +1149,7 @@ mod content {
Content::Newtype(v) => visitor.visit_newtype_struct(ContentDeserializer::new(*v)),
Content::Seq(v) => visit_content_seq(v, visitor),
Content::Map(v) => visit_content_map(v, visitor),
Content::Enum(v) => visitor.visit_enum(EnumDeserializer::new(v.0, Some(v.1))),
}
}

Expand Down Expand Up @@ -1747,6 +1751,9 @@ mod content {
}
Content::Seq(ref v) => visit_content_seq_ref(v, visitor),
Content::Map(ref v) => visit_content_map_ref(v, visitor),
Content::Enum(ref v) => {
visitor.visit_enum(EnumRefDeserializer::new(&v.0, Some(&v.1)))
}
}
}

Expand Down Expand Up @@ -2091,6 +2098,19 @@ mod content {
err: PhantomData<E>,
}

impl<'a, 'de: 'a, E> EnumRefDeserializer<'a, 'de, E>
where
E: de::Error,
{
fn new(variant: &'a Content<'de>, value: Option<&'a Content<'de>>) -> Self {
Self {
variant,
value,
err: PhantomData,
}
}
}

impl<'de, 'a, E> de::EnumAccess<'de> for EnumRefDeserializer<'a, 'de, E>
where
E: de::Error,
Expand Down

0 comments on commit 3394f7b

Please sign in to comment.