Merge pull request #1299 from dtolnay/flattenmap

Allow multiple flattened maps to see the same fields
This commit is contained in:
David Tolnay 2018-06-01 13:00:45 -07:00 committed by GitHub
commit d5ec3efe49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 16 deletions

View File

@ -2723,7 +2723,7 @@ where
where
V: Visitor<'de>,
{
visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), None))
visitor.visit_map(FlatMapAccess::new(self.0.iter()))
}
fn deserialize_struct<V>(
@ -2735,7 +2735,7 @@ where
where
V: Visitor<'de>,
{
visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), Some(fields)))
visitor.visit_map(FlatStructAccess::new(self.0.iter_mut(), fields))
}
fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value, Self::Error>
@ -2784,22 +2784,19 @@ where
#[cfg(any(feature = "std", feature = "alloc"))]
pub struct FlatMapAccess<'a, 'de: 'a, E> {
iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>,
pending_content: Option<Content<'de>>,
fields: Option<&'static [&'static str]>,
iter: slice::Iter<'a, Option<(Content<'de>, Content<'de>)>>,
pending_content: Option<&'a Content<'de>>,
_marker: PhantomData<E>,
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a, 'de, E> FlatMapAccess<'a, 'de, E> {
fn new(
iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>,
fields: Option<&'static [&'static str]>,
iter: slice::Iter<'a, Option<(Content<'de>, Content<'de>)>>,
) -> FlatMapAccess<'a, 'de, E> {
FlatMapAccess {
iter: iter,
pending_content: None,
fields: fields,
_marker: PhantomData,
}
}
@ -2812,6 +2809,61 @@ where
{
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() {
// Items in the vector are nulled out when used by a struct.
if let Some((ref key, ref content)) = *item {
self.pending_content = Some(content);
return seed.deserialize(ContentRefDeserializer::new(key)).map(Some);
}
}
Ok(None)
}
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'de>,
{
match self.pending_content.take() {
Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
None => Err(Error::custom("value is missing")),
}
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub struct FlatStructAccess<'a, 'de: 'a, E> {
iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>,
pending_content: Option<Content<'de>>,
fields: &'static [&'static str],
_marker: PhantomData<E>,
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a, 'de, E> FlatStructAccess<'a, 'de, E> {
fn new(
iter: slice::IterMut<'a, Option<(Content<'de>, Content<'de>)>>,
fields: &'static [&'static str],
) -> FlatStructAccess<'a, 'de, E> {
FlatStructAccess {
iter: iter,
pending_content: None,
fields: fields,
_marker: PhantomData,
}
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a, 'de, E> MapAccess<'de> for FlatStructAccess<'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>,
@ -2822,13 +2874,7 @@ where
// about. In case we do not know which fields we want, we take them all.
let use_item = match *item {
None => false,
Some((ref c, _)) => c.as_str().map_or(self.fields.is_none(), |key| {
match self.fields {
None => true,
Some(fields) if fields.contains(&key) => true,
_ => false,
}
}),
Some((ref c, _)) => c.as_str().map_or(false, |key| self.fields.contains(&key)),
};
if use_item {

View File

@ -14,7 +14,7 @@ extern crate serde_derive;
extern crate serde;
use self::serde::de::{self, Unexpected};
use self::serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use std::marker::PhantomData;
extern crate serde_test;
@ -1683,6 +1683,49 @@ fn test_complex_flatten() {
);
}
#[test]
fn test_flatten_map_twice() {
#[derive(Debug, PartialEq, Deserialize)]
struct Outer {
#[serde(flatten)]
first: BTreeMap<String, String>,
#[serde(flatten)]
between: Inner,
#[serde(flatten)]
second: BTreeMap<String, String>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct Inner {
y: String,
}
assert_de_tokens(
&Outer {
first: {
let mut first = BTreeMap::new();
first.insert("x".to_owned(), "X".to_owned());
first.insert("y".to_owned(), "Y".to_owned());
first
},
between: Inner { y: "Y".to_owned() },
second: {
let mut second = BTreeMap::new();
second.insert("x".to_owned(), "X".to_owned());
second
},
},
&[
Token::Map { len: None },
Token::Str("x"),
Token::Str("X"),
Token::Str("y"),
Token::Str("Y"),
Token::MapEnd,
],
);
}
#[test]
fn test_flatten_unsupported_type() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]