Support deserializing a flattened internally tagged enum

This commit is contained in:
David Tolnay
2018-05-05 21:25:54 -07:00
parent ed425b3a6f
commit d8120e19bc
4 changed files with 156 additions and 2 deletions
+14
View File
@@ -1132,6 +1132,20 @@ pub trait Deserializer<'de>: Sized {
fn is_human_readable(&self) -> bool {
true
}
// Not public API.
#[doc(hidden)]
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)
}
}
////////////////////////////////////////////////////////////////////////////////
+93
View File
@@ -2715,6 +2715,22 @@ where
byte_buf option unit unit_struct seq tuple tuple_struct identifier
ignored_any
}
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,
_marker: PhantomData,
})
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
@@ -2786,3 +2802,80 @@ 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,
_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
E: Error,
{
type Error = E;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
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);
}
Ok(None)
}
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, Self::Error>
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"),
}
}
}