Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to deserialize enums from SeqAccessDeserializer #2445

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
126 changes: 123 additions & 3 deletions serde/src/de/value.rs
Expand Up @@ -1051,6 +1051,12 @@ where
////////////////////////////////////////////////////////////////////////////////

/// A deserializer holding a `SeqAccess`.
///
/// This deserializer will call [`Visitor::visit_seq`] for all requests except
/// for [`Deserializer::deserialize_enum`]. In the latest case the enum will be
/// deserialized from the first two elements of a sequence. The first element
/// would be interpreted as a variant name and the second as a content of
/// a variant. In case of unit variant the second element is optional.
#[derive(Clone, Debug)]
pub struct SeqAccessDeserializer<A> {
seq: A,
Expand All @@ -1076,10 +1082,43 @@ where
visitor.visit_seq(self.seq)
}

fn deserialize_enum<V>(
self,
_name: &str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_enum(self)
}

forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
tuple_struct map struct identifier ignored_any
}
}

impl<'de, A> de::EnumAccess<'de> for SeqAccessDeserializer<A>
where
A: de::SeqAccess<'de>,
{
type Error = A::Error;
type Variant = private::SeqAsEnum<A>;

fn variant_seed<T>(mut self, seed: T) -> Result<(T::Value, Self::Variant), Self::Error>
where
T: de::DeserializeSeed<'de>,
{
match tri!(self.seq.next_element_seed(seed)) {
Some(key) => Ok((key, private::seq_as_enum(self.seq))),
None => Err(de::Error::invalid_length(
self.seq.size_hint().unwrap_or(0),
&"enum tag",
)),
}
}
}

Expand Down Expand Up @@ -1454,6 +1493,12 @@ where
////////////////////////////////////////////////////////////////////////////////

/// A deserializer holding a `MapAccess`.
///
/// This deserializer will call [`Visitor::visit_map`] for all requests except
/// for [`Deserializer::deserialize_enum`]. In the latest case the enum will be
/// deserialized from the first entry of a map. The map key would be interpreted
/// as a variant name and the map value would be interpreted as a content of
/// a variant.
#[derive(Clone, Debug)]
pub struct MapAccessDeserializer<A> {
map: A,
Expand Down Expand Up @@ -1511,7 +1556,10 @@ where
{
match tri!(self.map.next_key_seed(seed)) {
Some(key) => Ok((key, private::map_as_enum(self.map))),
None => Err(de::Error::invalid_type(de::Unexpected::Map, &"enum")),
None => Err(de::Error::invalid_length(
self.map.size_hint().unwrap_or(0),
&"enum",
)),
}
}
}
Expand Down Expand Up @@ -1557,7 +1605,8 @@ mod private {
use crate::lib::*;

use crate::de::{
self, DeserializeSeed, Deserializer, MapAccess, Unexpected, VariantAccess, Visitor,
self, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Unexpected, VariantAccess,
Visitor,
};

pub struct UnitOnly<E> {
Expand Down Expand Up @@ -1662,6 +1711,77 @@ mod private {
}
}

pub struct SeqAsEnum<A> {
seq: A,
}

pub fn seq_as_enum<A>(seq: A) -> SeqAsEnum<A> {
SeqAsEnum { seq }
}

impl<'de, A> VariantAccess<'de> for SeqAsEnum<A>
where
A: SeqAccess<'de>,
{
type Error = A::Error;

fn unit_variant(mut self) -> Result<(), Self::Error> {
// Even when content is missing, it is also acceptable
// So both ["Unit"] and ["Unit", ()] is acceptable
tri!(self.seq.next_element::<()>());
Ok(())
}

fn newtype_variant_seed<T>(mut self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'de>,
{
match tri!(self.seq.next_element_seed(seed)) {
Some(value) => Ok(value),
None => Err(de::Error::invalid_length(
self.seq.size_hint().unwrap_or(0) + 1,
&"content of newtype variant",
)),
}
}

fn tuple_variant<V>(mut self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
match tri!(self
.seq
.next_element_seed(SeedTupleVariant { len, visitor }))
{
Some(value) => Ok(value),
None => Err(de::Error::invalid_length(
self.seq.size_hint().unwrap_or(0) + 1,
&"content of tuple variant",
)),
}
}

fn struct_variant<V>(
mut self,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
match tri!(self
.seq
.next_element_seed(SeedStructVariant { visitor: visitor }))
{
Some(value) => Ok(value),
None => Err(de::Error::invalid_length(
self.seq.size_hint().unwrap_or(0) + 1,
&"content of struct variant",
)),
}
}
}

struct SeedTupleVariant<V> {
len: usize,
visitor: V,
Expand Down