Simplify implementation of flattened internally tagged enum

The fact that the tag entry is consumed was only observable if there is
a later flattened map field, at which point the behavior is already
confusing anyway.

    #[derive(Deserialize)]
    struct Example {
        #[serde(flatten)]
        a: InternallyTagged,
        #[serde(flatten)]
        rest: BTreeMap<String, Value>,
    }

Before this simplification the map would receive all the fields of the
internally tagged enum but not its tag, after it receives all the fields
including the tag.
This commit is contained in:
David Tolnay 2018-05-05 22:30:41 -07:00
parent d8120e19bc
commit aac1c65033
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
3 changed files with 13 additions and 54 deletions

View File

@ -1138,12 +1138,10 @@ pub trait Deserializer<'de>: Sized {
fn private_deserialize_internally_tagged_enum<V>( fn private_deserialize_internally_tagged_enum<V>(
self, self,
visitor: V, visitor: V,
tag: &'static str,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
let _ = tag;
self.deserialize_any(visitor) self.deserialize_any(visitor)
} }
} }

View File

@ -2719,15 +2719,13 @@ where
fn private_deserialize_internally_tagged_enum<V>( fn private_deserialize_internally_tagged_enum<V>(
self, self,
visitor: V, visitor: V,
tag: &'static str,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
visitor.visit_map(FlatInternallyTaggedAccess { visitor.visit_map(FlatInternallyTaggedAccess {
iter: self.0.iter_mut(), iter: self.0.iter_mut(),
pending: Pending::None, pending: None,
tag: tag,
_marker: PhantomData, _marker: PhantomData,
}) })
} }
@ -2806,18 +2804,10 @@ where
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
pub struct FlatInternallyTaggedAccess<'a, 'de: 'a, E> { pub struct FlatInternallyTaggedAccess<'a, 'de: 'a, E> {
iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>, iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>,
pending: Pending<'a, 'de>, pending: Option<&'a Content<'de>>,
tag: &'static str,
_marker: PhantomData<E>, _marker: PhantomData<E>,
} }
#[cfg(any(feature = "std", feature = "alloc"))]
enum Pending<'a, 'de: 'a> {
Content(Content<'de>),
ContentRef(&'a Content<'de>),
None,
}
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
impl<'a, 'de, E> MapAccess<'de> for FlatInternallyTaggedAccess<'a, 'de, E> impl<'a, 'de, E> MapAccess<'de> for FlatInternallyTaggedAccess<'a, 'de, E>
where where
@ -2830,32 +2820,13 @@ where
T: DeserializeSeed<'de>, T: DeserializeSeed<'de>,
{ {
while let Some(item) = self.iter.next() { while let Some(item) = self.iter.next() {
let is_tag = match *item { // Do not take(), instead borrow this entry. The internally tagged
Some((ref key, _)) => key.as_str().map_or(false, |key| key == self.tag), // enum does its own buffering so we can't tell whether this entry
None => continue, // is going to be consumed. Borrowing here leaves the entry
}; // available for later flattened fields.
let (ref key, ref content) = *item.as_ref().unwrap();
let ret = if is_tag { self.pending = Some(content);
let (key, content) = item.take().unwrap(); return seed.deserialize(ContentRefDeserializer::new(key)).map(Some);
self.pending = Pending::Content(content);
// Could use `ContentDeserializer::new(key)` here but we prefer
// to avoid instantiating `seed.deserialize` twice with
// different Deserializer type parameters. The visitor that
// decides which variant we are looking at does not benefit from
// having ownership of this string.
seed.deserialize(ContentRefDeserializer::new(&key))
} else {
// Do not take(), instead borrow this entry. The internally
// tagged enum does its own buffering so we can't tell whether
// this entry is going to be consumed. Borrowing here leaves the
// entry available for later flattened fields.
let (ref key, ref content) = *item.as_ref().unwrap();
self.pending = Pending::ContentRef(content);
seed.deserialize(ContentRefDeserializer::new(key))
};
return ret.map(Some);
} }
Ok(None) Ok(None)
} }
@ -2864,18 +2835,9 @@ where
where where
T: DeserializeSeed<'de>, T: DeserializeSeed<'de>,
{ {
match mem::replace(&mut self.pending, Pending::None) { match self.pending.take() {
Pending::Content(value) => { Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
// Could use `ContentDeserializer::new(value)` here but we None => panic!("value is missing"),
// prefer to avoid instantiating `seed.deserialize` twice with
// different Deserializer type parameters. Flatten and internal
// tagging are both relatively slow at runtime anyway so the
// improvement in compile time is more important here than
// potentially saving some string copies.
seed.deserialize(ContentRefDeserializer::new(&value))
}
Pending::ContentRef(value) => seed.deserialize(ContentRefDeserializer::new(value)),
Pending::None => panic!("value is missing"),
} }
} }
} }

View File

@ -1195,8 +1195,7 @@ fn deserialize_internally_tagged_enum(
let __tagged = try!(_serde::Deserializer::private_deserialize_internally_tagged_enum( let __tagged = try!(_serde::Deserializer::private_deserialize_internally_tagged_enum(
__deserializer, __deserializer,
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag), _serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
#tag));
match __tagged.tag { match __tagged.tag {
#(#variant_arms)* #(#variant_arms)*