diff --git a/serde/src/private/ser.rs b/serde/src/private/ser.rs index f19c4063..948fc2fa 100644 --- a/serde/src/private/ser.rs +++ b/serde/src/private/ser.rs @@ -11,7 +11,12 @@ use lib::*; use ser::{self, Impossible, Serialize, SerializeMap, SerializeStruct, Serializer}; #[cfg(any(feature = "std", feature = "alloc"))] -use self::content::{SerializeStructVariantAsMapValue, SerializeTupleVariantAsMapValue}; +use self::content::{ + SerializeStructVariantAsMapValue, + SerializeTupleVariantAsMapValue, + ContentSerializer, + Content, +}; /// Used to check that serde(getter) attributes return the expected type. /// Not public API. @@ -461,7 +466,7 @@ mod content { } #[derive(Debug)] - enum Content { + pub enum Content { Bool(bool), U8(u8), @@ -586,12 +591,12 @@ mod content { } } - struct ContentSerializer { + pub struct ContentSerializer { error: PhantomData, } impl ContentSerializer { - fn new() -> Self { + pub fn new() -> Self { ContentSerializer { error: PhantomData } } } @@ -806,7 +811,7 @@ mod content { } } - struct SerializeSeq { + pub struct SerializeSeq { elements: Vec, error: PhantomData, } @@ -832,7 +837,7 @@ mod content { } } - struct SerializeTuple { + pub struct SerializeTuple { elements: Vec, error: PhantomData, } @@ -858,7 +863,7 @@ mod content { } } - struct SerializeTupleStruct { + pub struct SerializeTupleStruct { name: &'static str, fields: Vec, error: PhantomData, @@ -885,7 +890,7 @@ mod content { } } - struct SerializeTupleVariant { + pub struct SerializeTupleVariant { name: &'static str, variant_index: u32, variant: &'static str, @@ -919,7 +924,7 @@ mod content { } } - struct SerializeMap { + pub struct SerializeMap { entries: Vec<(Content, Content)>, key: Option, error: PhantomData, @@ -969,7 +974,7 @@ mod content { } } - struct SerializeStruct { + pub struct SerializeStruct { name: &'static str, fields: Vec<(&'static str, Content)>, error: PhantomData, @@ -996,7 +1001,7 @@ mod content { } } - struct SerializeStructVariant { + pub struct SerializeStructVariant { name: &'static str, variant_index: u32, variant: &'static str, @@ -1059,7 +1064,7 @@ impl<'a, M> Serializer for FlatMapSerializer<'a, M> type SerializeMap = FlatMapSerializeMap<'a, M>; type SerializeStruct = FlatMapSerializeStruct<'a, M>; type SerializeTupleVariant = Impossible; - type SerializeStructVariant = Impossible; + type SerializeStructVariant = FlatMapSerializeStructVariantAsMapValue<'a, M>; fn serialize_bool(self, _: bool) -> Result { Err(self.bad_type(Unsupported::Boolean)) @@ -1212,10 +1217,11 @@ impl<'a, M> Serializer for FlatMapSerializer<'a, M> self, _: &'static str, _: u32, - _: &'static str, + inner_variant: &'static str, _: usize, ) -> Result { - Err(self.bad_type(Unsupported::Enum)) + try!(self.0.serialize_key(inner_variant)); + Ok(FlatMapSerializeStructVariantAsMapValue::new(self.0, inner_variant)) } } @@ -1269,3 +1275,44 @@ impl<'a, M> ser::SerializeStruct for FlatMapSerializeStruct<'a, M> Ok(()) } } + +#[cfg(any(feature = "std", feature = "alloc"))] +pub struct FlatMapSerializeStructVariantAsMapValue<'a, M: 'a> { + map: &'a mut M, + name: &'static str, + fields: Vec<(&'static str, Content)>, +} + +impl<'a, M> FlatMapSerializeStructVariantAsMapValue<'a, M> + where M: SerializeMap + 'a +{ + fn new(map: &'a mut M, name: &'static str) -> FlatMapSerializeStructVariantAsMapValue<'a, M> { + FlatMapSerializeStructVariantAsMapValue { + map: map, + name: name, + fields: Vec::new(), + } + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, M> ser::SerializeStructVariant for FlatMapSerializeStructVariantAsMapValue<'a, M> + where M: SerializeMap + 'a +{ + type Ok = (); + type Error = M::Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + let value = try!(value.serialize(ContentSerializer::::new())); + self.fields.push((key, value)); + Ok(()) + } + + fn end(self) -> Result<(), Self::Error> { + try!(self.map.serialize_value(&Content::Struct(self.name, self.fields))); + Ok(()) + } +} diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index c11b02bb..e3e91e1f 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -96,7 +96,7 @@ where } #[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(repr="map")] +#[serde(repr = "map")] struct CollectOther { a: u32, b: u32, @@ -104,6 +104,27 @@ struct CollectOther { extra: HashMap, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(repr = "map")] +struct ChangeRequest { + #[serde(flatten)] + data: ChangeAction, + #[serde(flatten)] + extra: HashMap, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +enum ChangeAction { + AppendInteger { + value: u32 + }, + InsertInteger { + index: u32, + value: u32 + }, +} + #[test] fn test_default_struct() { assert_de_tokens( @@ -1309,3 +1330,48 @@ fn test_collect_other() { ], ); } + +#[test] +fn test_flatten_struct_enum() { + let mut extra = HashMap::new(); + extra.insert("extra_key".into(), "extra value".into()); + let change_request = ChangeRequest { + data: ChangeAction::InsertInteger { + index: 0, + value: 42 + }, + extra: extra, + }; + assert_de_tokens( + &change_request, + &[ + Token::Map { len: None }, + Token::Str("insert_integer"), + Token::Map { len: None }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::MapEnd, + Token::Str("extra_key"), + Token::String("extra value".into()), + Token::MapEnd + ], + ); + assert_ser_tokens( + &change_request, + &[ + Token::Map { len: None }, + Token::Str("insert_integer"), + Token::Struct { len: 2, name: "insert_integer" }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::StructEnd, + Token::Str("extra_key"), + Token::String("extra value".into()), + Token::MapEnd + ], + ); +}