Merge pull request #1299 from dtolnay/flattenmap
Allow multiple flattened maps to see the same fields
This commit is contained in:
commit
d5ec3efe49
@ -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 {
|
||||
|
@ -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)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user