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>(
self,
visitor: V,
tag: &'static str,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let _ = tag;
self.deserialize_any(visitor)
}
}

View File

@ -2719,15 +2719,13 @@ where
fn private_deserialize_internally_tagged_enum<V>(
self,
visitor: V,
tag: &'static str,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(FlatInternallyTaggedAccess {
iter: self.0.iter_mut(),
pending: Pending::None,
tag: tag,
pending: None,
_marker: PhantomData,
})
}
@ -2806,18 +2804,10 @@ where
#[cfg(any(feature = "std", feature = "alloc"))]
pub struct FlatInternallyTaggedAccess<'a, 'de: 'a, E> {
iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>,
pending: Pending<'a, 'de>,
tag: &'static str,
pending: Option<&'a Content<'de>>,
_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"))]
impl<'a, 'de, E> MapAccess<'de> for FlatInternallyTaggedAccess<'a, 'de, E>
where
@ -2830,32 +2820,13 @@ where
T: DeserializeSeed<'de>,
{
while let Some(item) = self.iter.next() {
let is_tag = match *item {
Some((ref key, _)) => key.as_str().map_or(false, |key| key == self.tag),
None => continue,
};
let ret = if is_tag {
let (key, content) = item.take().unwrap();
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);
// 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 = Some(content);
return seed.deserialize(ContentRefDeserializer::new(key)).map(Some);
}
Ok(None)
}
@ -2864,18 +2835,9 @@ where
where
T: DeserializeSeed<'de>,
{
match mem::replace(&mut self.pending, Pending::None) {
Pending::Content(value) => {
// Could use `ContentDeserializer::new(value)` here but we
// 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"),
match self.pending.take() {
Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
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(
__deserializer,
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag),
#tag));
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
match __tagged.tag {
#(#variant_arms)*