diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index a3a55d8e..6eae7311 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -11,13 +11,13 @@ use lib::*; use de::{Deserialize, DeserializeSeed, Deserializer, Error, IntoDeserializer, Visitor}; #[cfg(any(feature = "std", feature = "alloc"))] -use de::Unexpected; +use de::{Unexpected, MapAccess}; #[cfg(any(feature = "std", feature = "alloc"))] pub use self::content::{Content, ContentDeserializer, ContentRefDeserializer, InternallyTaggedUnitVisitor, TagContentOtherField, TagContentOtherFieldVisitor, TagOrContentField, TagOrContentFieldVisitor, - TaggedContentVisitor, UntaggedUnitVisitor}; + TaggedContentVisitor, UntaggedUnitVisitor, EnumDeserializer}; /// If the missing field is of type `Option` then treat is as `None`, /// otherwise it is an error. @@ -269,6 +269,14 @@ mod content { } impl<'de> Content<'de> { + pub fn as_str(&self) -> Option<&str> { + match *self { + Content::Str(x) => Some(x), + Content::String(ref x) => Some(x), + _ => None, + } + } + fn unexpected(&self) -> Unexpected { match *self { Content::Bool(b) => Unexpected::Bool(b), @@ -1118,11 +1126,7 @@ mod content { } }; - visitor.visit_enum(EnumDeserializer { - variant: variant, - value: value, - err: PhantomData, - }) + visitor.visit_enum(EnumDeserializer::new(variant, value)) } fn deserialize_unit_struct( @@ -1170,7 +1174,7 @@ mod content { } } - struct EnumDeserializer<'de, E> + pub struct EnumDeserializer<'de, E> where E: de::Error, { @@ -1179,6 +1183,18 @@ mod content { err: PhantomData, } + impl<'de, E> EnumDeserializer<'de, E> + where E: de::Error + { + pub fn new(variant: Content<'de>, value: Option>) -> EnumDeserializer<'de, E> { + EnumDeserializer { + variant: variant, + value: value, + err: PhantomData, + } + } + } + impl<'de, E> de::EnumAccess<'de> for EnumDeserializer<'de, E> where E: de::Error, @@ -1199,7 +1215,7 @@ mod content { } } - struct VariantDeserializer<'de, E> + pub struct VariantDeserializer<'de, E> where E: de::Error, { @@ -2062,3 +2078,162 @@ where T::deserialize_in_place(deserializer, self.0) } } + +#[cfg(any(feature = "std", feature = "alloc"))] +pub struct FlatMapDeserializer<'a, 'de: 'a, E>( + pub &'a mut Vec, Content<'de>)>>, + pub PhantomData +); + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, 'de, E> Deserializer<'de> for FlatMapDeserializer<'a, 'de, E> + where E: Error +{ + type Error = E; + + fn deserialize_any(self, _: V) -> Result + where + V: Visitor<'de>, + { + Err(Error::custom("can only flatten structs and maps")) + } + + fn deserialize_enum( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V + ) -> Result + where + V: Visitor<'de>, + { + for item in self.0.iter_mut() { + // items in the vector are nulled out when used. So we can only use + // an item if it's still filled in and if the field is one we care + // about. + let use_item = match *item { + None => false, + Some((ref c, _)) => c.as_str().map_or(false, |x| variants.contains(&x)) + }; + + if use_item { + let (key, value) = item.take().unwrap(); + return visitor.visit_enum(EnumDeserializer::new( + key, + Some(value) + )); + } + } + + Err(Error::custom(format_args!( + "no variant of enum {} not found in flattened data", + name + ))) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), None)) + } + + fn deserialize_struct( + self, + _: &'static str, + fields: &'static [&'static str], + visitor: V + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_map(FlatMapAccess::new(self.0.iter_mut(), Some(fields))) + } + + fn deserialize_newtype_struct( + self, + _name: &str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes + byte_buf option unit unit_struct seq tuple tuple_struct identifier + ignored_any + } +} + +#[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>, + fields: Option<&'static [&'static str]>, + _marker: PhantomData, +} + +#[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]> + ) -> FlatMapAccess<'a, 'de, E> { + FlatMapAccess { + iter: iter, + pending_content: None, + fields: fields, + _marker: PhantomData, + } + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, 'de, E> MapAccess<'de> for FlatMapAccess<'a, 'de, E> + where E: Error +{ + type Error = E; + + fn next_key_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: DeserializeSeed<'de>, + { + while let Some(item) = self.iter.next() { + // items in the vector are nulled out when used. So we can only use + // an item if it's still filled in and if the field is one we care + // 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 + } + }) + } + }; + + if use_item { + let (key, content) = item.take().unwrap(); + self.pending_content = Some(content); + return seed.deserialize(ContentDeserializer::new(key)).map(Some); + } + } + Ok(None) + } + + fn next_value_seed(&mut self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + match self.pending_content.take() { + Some(value) => seed.deserialize(ContentDeserializer::new(value)), + None => Err(Error::custom("value is missing")), + } + } +} diff --git a/serde/src/private/ser.rs b/serde/src/private/ser.rs index 932ebf4a..5d743808 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. @@ -58,10 +63,11 @@ enum Unsupported { ByteArray, Optional, Unit, + #[cfg(any(feature = "std", feature = "alloc"))] + UnitStruct, Sequence, Tuple, TupleStruct, - #[cfg(not(any(feature = "std", feature = "alloc")))] Enum, } @@ -76,10 +82,11 @@ impl Display for Unsupported { Unsupported::ByteArray => formatter.write_str("a byte array"), Unsupported::Optional => formatter.write_str("an optional"), Unsupported::Unit => formatter.write_str("unit"), + #[cfg(any(feature = "std", feature = "alloc"))] + Unsupported::UnitStruct => formatter.write_str("unit struct"), Unsupported::Sequence => formatter.write_str("a sequence"), Unsupported::Tuple => formatter.write_str("a tuple"), Unsupported::TupleStruct => formatter.write_str("a tuple struct"), - #[cfg(not(any(feature = "std", feature = "alloc")))] Unsupported::Enum => formatter.write_str("an enum"), } } @@ -459,7 +466,7 @@ mod content { } #[derive(Debug)] - enum Content { + pub enum Content { Bool(bool), U8(u8), @@ -584,12 +591,12 @@ mod content { } } - struct ContentSerializer { + pub struct ContentSerializer { error: PhantomData, } impl ContentSerializer { - fn new() -> Self { + pub fn new() -> Self { ContentSerializer { error: PhantomData } } } @@ -804,7 +811,7 @@ mod content { } } - struct SerializeSeq { + pub struct SerializeSeq { elements: Vec, error: PhantomData, } @@ -830,7 +837,7 @@ mod content { } } - struct SerializeTuple { + pub struct SerializeTuple { elements: Vec, error: PhantomData, } @@ -856,7 +863,7 @@ mod content { } } - struct SerializeTupleStruct { + pub struct SerializeTupleStruct { name: &'static str, fields: Vec, error: PhantomData, @@ -883,7 +890,7 @@ mod content { } } - struct SerializeTupleVariant { + pub struct SerializeTupleVariant { name: &'static str, variant_index: u32, variant: &'static str, @@ -917,7 +924,7 @@ mod content { } } - struct SerializeMap { + pub struct SerializeMap { entries: Vec<(Content, Content)>, key: Option, error: PhantomData, @@ -967,7 +974,7 @@ mod content { } } - struct SerializeStruct { + pub struct SerializeStruct { name: &'static str, fields: Vec<(&'static str, Content)>, error: PhantomData, @@ -994,7 +1001,7 @@ mod content { } } - struct SerializeStructVariant { + pub struct SerializeStructVariant { name: &'static str, variant_index: u32, variant: &'static str, @@ -1028,3 +1035,283 @@ mod content { } } } + +#[cfg(any(feature = "std", feature = "alloc"))] +pub struct FlatMapSerializer<'a, M: 'a>(pub &'a mut M); + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, M> FlatMapSerializer<'a, M> +where + M: SerializeMap + 'a +{ + fn bad_type(self, what: Unsupported) -> M::Error { + ser::Error::custom(format_args!("can only flatten structs and maps (got {})", what)) + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, M> Serializer for FlatMapSerializer<'a, M> + where M: SerializeMap + 'a +{ + type Ok = (); + type Error = M::Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeMap = FlatMapSerializeMap<'a, M>; + type SerializeStruct = FlatMapSerializeStruct<'a, M>; + type SerializeTupleVariant = Impossible; + type SerializeStructVariant = FlatMapSerializeStructVariantAsMapValue<'a, M>; + + fn serialize_bool(self, _: bool) -> Result { + Err(self.bad_type(Unsupported::Boolean)) + } + + fn serialize_i8(self, _: i8) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_i16(self, _: i16) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_i32(self, _: i32) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_i64(self, _: i64) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_u8(self, _: u8) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_u16(self, _: u16) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_u32(self, _: u32) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_u64(self, _: u64) -> Result { + Err(self.bad_type(Unsupported::Integer)) + } + + fn serialize_f32(self, _: f32) -> Result { + Err(self.bad_type(Unsupported::Float)) + } + + fn serialize_f64(self, _: f64) -> Result { + Err(self.bad_type(Unsupported::Float)) + } + + fn serialize_char(self, _: char) -> Result { + Err(self.bad_type(Unsupported::Char)) + } + + fn serialize_str(self, _: &str) -> Result { + Err(self.bad_type(Unsupported::String)) + } + + fn serialize_bytes(self, _: &[u8]) -> Result { + Err(self.bad_type(Unsupported::ByteArray)) + } + + fn serialize_none(self) -> Result { + Err(self.bad_type(Unsupported::Optional)) + } + + fn serialize_some(self, _: &T) -> Result + where + T: Serialize, + { + Err(self.bad_type(Unsupported::Optional)) + } + + fn serialize_unit(self) -> Result { + Err(self.bad_type(Unsupported::Unit)) + } + + fn serialize_unit_struct(self, _: &'static str) -> Result { + Err(self.bad_type(Unsupported::UnitStruct)) + } + + fn serialize_unit_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + ) -> Result { + Err(self.bad_type(Unsupported::Enum)) + } + + fn serialize_newtype_struct( + self, + _: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _: &'static str, + _: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + try!(self.0.serialize_key(variant)); + self.0.serialize_value(value) + } + + fn serialize_seq(self, _: Option) -> Result { + Err(self.bad_type(Unsupported::Sequence)) + } + + fn serialize_tuple(self, _: usize) -> Result { + Err(self.bad_type(Unsupported::Tuple)) + } + + fn serialize_tuple_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + Err(self.bad_type(Unsupported::TupleStruct)) + } + + fn serialize_tuple_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Err(self.bad_type(Unsupported::Enum)) + } + + fn serialize_map(self, _: Option) -> Result { + Ok(FlatMapSerializeMap(self.0)) + } + + fn serialize_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + Ok(FlatMapSerializeStruct(self.0)) + } + + fn serialize_struct_variant( + self, + _: &'static str, + _: u32, + inner_variant: &'static str, + _: usize, + ) -> Result { + try!(self.0.serialize_key(inner_variant)); + Ok(FlatMapSerializeStructVariantAsMapValue::new(self.0, inner_variant)) + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +pub struct FlatMapSerializeMap<'a, M: 'a>(&'a mut M); + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, M> ser::SerializeMap for FlatMapSerializeMap<'a, M> + where M: SerializeMap + 'a +{ + type Ok = (); + type Error = M::Error; + + fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + self.0.serialize_key(key) + } + + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + self.0.serialize_value(value) + } + + fn end(self) -> Result<(), Self::Error> { + Ok(()) + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +pub struct FlatMapSerializeStruct<'a, M: 'a>(&'a mut M); + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, M> ser::SerializeStruct for FlatMapSerializeStruct<'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, + { + self.0.serialize_entry(key, value) + } + + fn end(self) -> Result<(), Self::Error> { + 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)>, +} + +#[cfg(any(feature = "std", feature = "alloc"))] +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/serde_derive/src/de.rs b/serde_derive/src/de.rs index 2f07d7e0..456773a8 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -268,7 +268,11 @@ fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option { - deserialize_struct_in_place(None, params, fields, &cont.attrs, None) + if let Some(code) = deserialize_struct_in_place(None, params, fields, &cont.attrs, None) { + code + } else { + return None; + } } Data::Struct(Style::Tuple, ref fields) | Data::Struct(Style::Newtype, ref fields) => { deserialize_tuple_in_place(None, params, fields, &cont.attrs, None) @@ -345,6 +349,8 @@ fn deserialize_tuple( split_with_de_lifetime(params); let delife = params.borrowed.de_lifetime(); + debug_assert!(!cattrs.has_flatten()); + // If there are getters (implying private fields), construct the local type // and use an `Into` conversion to get the remote type. If there are no // getters then construct the target type directly. @@ -440,6 +446,8 @@ fn deserialize_tuple_in_place( split_with_de_lifetime(params); let delife = params.borrowed.de_lifetime(); + debug_assert!(!cattrs.has_flatten()); + let is_enum = variant_ident.is_some(); let expecting = match variant_ident { Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident), @@ -522,6 +530,8 @@ fn deserialize_seq( ) -> Fragment { let vars = (0..fields.len()).map(field_i as fn(_) -> _); + // XXX: do we need an error for flattening here? + let deserialized_count = fields .iter() .filter(|field| !field.attrs.skip_deserializing()) @@ -613,6 +623,8 @@ fn deserialize_seq_in_place( ) -> Fragment { let vars = (0..fields.len()).map(field_i as fn(_) -> _); + // XXX: do we need an error for flattening here? + let deserialized_count = fields .iter() .filter(|field| !field.attrs.skip_deserializing()) @@ -793,10 +805,13 @@ fn deserialize_struct( let visit_seq = Stmts(deserialize_seq(&type_path, params, fields, true, cattrs)); - let (field_visitor, fields_stmt, visit_map) = - deserialize_struct_visitor(&type_path, params, fields, cattrs); + let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() { + deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs) + } else { + deserialize_struct_as_struct_visitor(&type_path, params, fields, cattrs) + }; let field_visitor = Stmts(field_visitor); - let fields_stmt = Stmts(fields_stmt); + let fields_stmt = fields_stmt.map(Stmts); let visit_map = Stmts(visit_map); let visitor_expr = quote! { @@ -813,6 +828,10 @@ fn deserialize_struct( quote! { _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) } + } else if cattrs.has_flatten() { + quote! { + _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) + } } else { let type_name = cattrs.name().deserialize_name(); quote! { @@ -827,10 +846,10 @@ fn deserialize_struct( quote!(mut __seq) }; - // untagged struct variants do not get a visit_seq method + // untagged struct variants do not get a visit_seq method. The same applies to structs that + // only have a map representation. let visit_seq = match *untagged { - Untagged::Yes => None, - Untagged::No => Some(quote! { + Untagged::No if !cattrs.has_flatten() => Some(quote! { #[inline] fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result where __A: _serde::de::SeqAccess<#delife> @@ -838,6 +857,7 @@ fn deserialize_struct( #visit_seq } }), + _ => None, }; quote_block! { @@ -878,9 +898,15 @@ fn deserialize_struct_in_place( fields: &[Field], cattrs: &attr::Container, deserializer: Option, -) -> Fragment { +) -> Option { let is_enum = variant_ident.is_some(); + // for now we do not support in_place deserialization for structs that + // are represented as map. + if cattrs.has_flatten() { + return None; + } + let this = ¶ms.this; let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params); @@ -893,10 +919,11 @@ fn deserialize_struct_in_place( let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs)); - let (field_visitor, fields_stmt, visit_map) = - deserialize_struct_in_place_visitor(params, fields, cattrs); + let (field_visitor, fields_stmt, visit_map) = deserialize_struct_as_struct_in_place_visitor( + params, fields, cattrs); + let field_visitor = Stmts(field_visitor); - let fields_stmt = Stmts(fields_stmt); + let fields_stmt = fields_stmt.map(Stmts); let visit_map = Stmts(visit_map); let visitor_expr = quote! { @@ -940,7 +967,7 @@ fn deserialize_struct_in_place( let in_place_ty_generics = de_ty_generics.in_place(); let place_life = place_lifetime(); - quote_block! { + Some(quote_block! { #field_visitor struct __Visitor #in_place_impl_generics #where_clause { @@ -968,7 +995,7 @@ fn deserialize_struct_in_place( #fields_stmt #dispatch - } + }) } fn deserialize_enum( @@ -1688,12 +1715,16 @@ fn deserialize_untagged_newtype_variant( fn deserialize_generated_identifier( fields: &[(String, Ident)], cattrs: &attr::Container, - is_variant: bool, + is_variant: bool ) -> Fragment { let this = quote!(__Field); let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident)| ident).collect(); - let (ignore_variant, fallthrough) = if is_variant || cattrs.deny_unknown_fields() { + let (ignore_variant, fallthrough) = if cattrs.has_flatten() { + let ignore_variant = quote!(__other(_serde::private::de::Content<'de>),); + let fallthrough = quote!(_serde::export::Ok(__Field::__other(__value))); + (Some(ignore_variant), Some(fallthrough)) + } else if is_variant || cattrs.deny_unknown_fields() { (None, None) } else { let ignore_variant = quote!(__ignore,); @@ -1706,11 +1737,18 @@ fn deserialize_generated_identifier( fields, is_variant, fallthrough, + cattrs.has_flatten(), )); + let lifetime = if cattrs.has_flatten() { + Some(quote!(<'de>)) + } else { + None + }; + quote_block! { #[allow(non_camel_case_types)] - enum __Field { + enum __Field #lifetime { #(#field_idents,)* #ignore_variant } @@ -1718,12 +1756,12 @@ fn deserialize_generated_identifier( struct __FieldVisitor; impl<'de> _serde::de::Visitor<'de> for __FieldVisitor { - type Value = __Field; + type Value = __Field #lifetime; #visitor_impl } - impl<'de> _serde::Deserialize<'de> for __Field { + impl<'de> _serde::Deserialize<'de> for __Field #lifetime { #[inline] fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result where __D: _serde::Deserializer<'de> @@ -1804,6 +1842,7 @@ fn deserialize_custom_identifier( &names_idents, is_variant, fallthrough, + false, )); quote_block! { @@ -1833,9 +1872,12 @@ fn deserialize_identifier( fields: &[(String, Ident)], is_variant: bool, fallthrough: Option, + collect_other_fields: bool ) -> Fragment { let field_strs = fields.iter().map(|&(ref name, _)| name); + let field_borrowed_strs = fields.iter().map(|&(ref name, _)| name); let field_bytes = fields.iter().map(|&(ref name, _)| Literal::byte_string(name.as_bytes())); + let field_borrowed_bytes = fields.iter().map(|&(ref name, _)| Literal::byte_string(name.as_bytes())); let constructors: &Vec<_> = &fields .iter() @@ -1852,28 +1894,129 @@ fn deserialize_identifier( let variant_indices = 0u64..; let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len()); - let visit_index = quote! { - fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result - where __E: _serde::de::Error - { - match __value { - #( - #variant_indices => _serde::export::Ok(#constructors), - )* - _ => _serde::export::Err(_serde::de::Error::invalid_value( - _serde::de::Unexpected::Unsigned(__value), - &#fallthrough_msg)) + let visit_other = if collect_other_fields { + Some(quote! { + fn visit_bool<__E>(self, __value: bool) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::Bool(__value))) } - } + + fn visit_i8<__E>(self, __value: i8) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I8(__value))) + } + + fn visit_i16<__E>(self, __value: i16) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I16(__value))) + } + + fn visit_i32<__E>(self, __value: i32) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I32(__value))) + } + + fn visit_i64<__E>(self, __value: i64) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I64(__value))) + } + + fn visit_u8<__E>(self, __value: u8) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U8(__value))) + } + + fn visit_u16<__E>(self, __value: u16) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U16(__value))) + } + + fn visit_u32<__E>(self, __value: u32) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U32(__value))) + } + + fn visit_u64<__E>(self, __value: u64) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U64(__value))) + } + + fn visit_f32<__E>(self, __value: f32) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::F32(__value))) + } + + fn visit_f64<__E>(self, __value: f64) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::F64(__value))) + } + + fn visit_char<__E>(self, __value: char) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::Char(__value))) + } + + fn visit_unit<__E>(self) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::Unit)) + } + }) + } else { + Some(quote! { + fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result + where __E: _serde::de::Error + { + match __value { + #( + #variant_indices => _serde::export::Ok(#constructors), + )* + _ => _serde::export::Err(_serde::de::Error::invalid_value( + _serde::de::Unexpected::Unsigned(__value), + &#fallthrough_msg)) + } + } + }) }; - let bytes_to_str = if fallthrough.is_some() { + let bytes_to_str = if fallthrough.is_some() || collect_other_fields { None } else { - let conversion = quote! { + Some(quote! { let __value = &_serde::export::from_utf8_lossy(__value); - }; - Some(conversion) + }) + }; + + let (value_as_str_content, value_as_borrowed_str_content, + value_as_bytes_content, value_as_borrowed_bytes_content) = if !collect_other_fields { + (None, None, None, None) + } else { + ( + Some(quote! { + let __value = _serde::private::de::Content::String(__value.to_string()); + }), + Some(quote! { + let __value = _serde::private::de::Content::Str(__value); + }), + Some(quote! { + let __value = _serde::private::de::Content::ByteBuf(__value.to_vec()); + }), + Some(quote! { + let __value = _serde::private::de::Content::Bytes(__value); + }) + ) }; let fallthrough_arm = if let Some(fallthrough) = fallthrough { @@ -1893,7 +2036,7 @@ fn deserialize_identifier( _serde::export::Formatter::write_str(formatter, #expecting) } - #visit_index + #visit_other fn visit_str<__E>(self, __value: &str) -> _serde::export::Result where __E: _serde::de::Error @@ -1902,7 +2045,39 @@ fn deserialize_identifier( #( #field_strs => _serde::export::Ok(#constructors), )* - _ => #fallthrough_arm + _ => { + #value_as_str_content + #fallthrough_arm + } + } + } + + fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::export::Result + where __E: _serde::de::Error + { + match __value { + #( + #field_borrowed_strs => _serde::export::Ok(#constructors), + )* + _ => { + #value_as_borrowed_str_content + #fallthrough_arm + } + } + } + + fn visit_borrowed_bytes<__E>(self, __value: &'de [u8]) -> _serde::export::Result + where __E: _serde::de::Error + { + match __value { + #( + #field_borrowed_bytes => _serde::export::Ok(#constructors), + )* + _ => { + #bytes_to_str + #value_as_borrowed_bytes_content + #fallthrough_arm + } } } @@ -1915,6 +2090,7 @@ fn deserialize_identifier( )* _ => { #bytes_to_str + #value_as_bytes_content #fallthrough_arm } } @@ -1922,12 +2098,14 @@ fn deserialize_identifier( } } -fn deserialize_struct_visitor( +fn deserialize_struct_as_struct_visitor( struct_path: &Tokens, params: &Parameters, fields: &[Field], cattrs: &attr::Container, -) -> (Fragment, Fragment, Fragment) { +) -> (Fragment, Option, Fragment) { + debug_assert!(!cattrs.has_flatten()); + let field_names_idents: Vec<_> = fields .iter() .enumerate() @@ -1946,7 +2124,27 @@ fn deserialize_struct_visitor( let visit_map = deserialize_map(struct_path, params, fields, cattrs); - (field_visitor, fields_stmt, visit_map) + (field_visitor, Some(fields_stmt), visit_map) +} + +fn deserialize_struct_as_map_visitor( + struct_path: &Tokens, + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, +) -> (Fragment, Option, Fragment) { + let field_names_idents: Vec<_> = fields + .iter() + .enumerate() + .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) + .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i))) + .collect(); + + let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false); + + let visit_map = deserialize_map(struct_path, params, fields, cattrs); + + (field_visitor, None, visit_map) } fn deserialize_map( @@ -1965,7 +2163,7 @@ fn deserialize_map( // Declare each field that will be deserialized. let let_values = fields_names .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) .map(|&(field, ref name)| { let field_ty = &field.ty; quote! { @@ -1973,10 +2171,22 @@ fn deserialize_map( } }); + // Collect contents for flatten fields into a buffer + let let_collect = if cattrs.has_flatten() { + Some(quote! { + let mut __collect = Vec::>::new(); + }) + } else { + None + }; + // Match arms to extract a value for a field. let value_arms = fields_names .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) .map(|&(field, ref name)| { let deser_name = field.attrs.name().deserialize_name(); @@ -2008,7 +2218,15 @@ fn deserialize_map( }); // Visit ignored values to consume them - let ignored_arm = if cattrs.deny_unknown_fields() { + let ignored_arm = if cattrs.has_flatten() { + Some(quote! { + __Field::__other(__name) => { + __collect.push(Some(( + __name, + try!(_serde::de::MapAccess::next_value(&mut __map))))); + } + }) + } else if cattrs.deny_unknown_fields() { None } else { Some(quote! { @@ -2038,7 +2256,7 @@ fn deserialize_map( let extract_values = fields_names .iter() - .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) .map(|&(field, ref name)| { let missing_expr = Match(expr_is_missing(field, cattrs)); @@ -2050,6 +2268,35 @@ fn deserialize_map( } }); + let extract_collected = fields_names + .iter() + .filter(|&&(field, _)| field.attrs.flatten()) + .map(|&(field, ref name)| { + let field_ty = field.ty; + quote! { + let #name: #field_ty = try!(_serde::de::Deserialize::deserialize( + _serde::private::de::FlatMapDeserializer( + &mut __collect, + _serde::export::PhantomData))); + } + }); + + let collected_deny_unknown_fields = if cattrs.has_flatten() && cattrs.deny_unknown_fields() { + Some(quote! { + if let Some(Some((__key, _))) = __collect.into_iter().filter(|x| x.is_some()).next() { + if let Some(__key) = __key.as_str() { + return _serde::export::Err( + _serde::de::Error::custom(format_args!("unknown field `{}`", &__key))); + } else { + return _serde::export::Err( + _serde::de::Error::custom(format_args!("unexpected map key"))); + } + } + }) + } else { + None + }; + let result = fields_names.iter().map(|&(field, ref name)| { let ident = field.ident.expect("struct contains unnamed fields"); if field.attrs.skip_deserializing() { @@ -2085,22 +2332,30 @@ fn deserialize_map( quote_block! { #(#let_values)* + #let_collect + #match_keys #let_default #(#extract_values)* + #(#extract_collected)* + + #collected_deny_unknown_fields + _serde::export::Ok(#result) } } #[cfg(feature = "deserialize_in_place")] -fn deserialize_struct_in_place_visitor( +fn deserialize_struct_as_struct_in_place_visitor( params: &Parameters, fields: &[Field], cattrs: &attr::Container, -) -> (Fragment, Fragment, Fragment) { +) -> (Fragment, Option, Fragment) { + debug_assert!(!cattrs.has_flatten()); + let field_names_idents: Vec<_> = fields .iter() .enumerate() @@ -2119,7 +2374,7 @@ fn deserialize_struct_in_place_visitor( let visit_map = deserialize_map_in_place(params, fields, cattrs); - (field_visitor, fields_stmt, visit_map) + (field_visitor, Some(fields_stmt), visit_map) } #[cfg(feature = "deserialize_in_place")] @@ -2128,6 +2383,8 @@ fn deserialize_map_in_place( fields: &[Field], cattrs: &attr::Container, ) -> Fragment { + debug_assert!(!cattrs.has_flatten()); + // Create the field names for the fields. let fields_names: Vec<_> = fields .iter() @@ -2145,6 +2402,18 @@ fn deserialize_map_in_place( } }); + // Collect contents for flatten fields into a buffer + let let_collect = if cattrs.has_flatten() { + Some(quote! { + let mut __collect = Vec::>::new(); + }) + } else { + None + }; + // Match arms to extract a value for a field. let value_arms_from = fields_names .iter() @@ -2179,7 +2448,13 @@ fn deserialize_map_in_place( }); // Visit ignored values to consume them - let ignored_arm = if cattrs.deny_unknown_fields() { + let ignored_arm = if cattrs.has_flatten() { + Some(quote! { + __Field::__other(__name) => { + __collect.push(Some((__name, try!(_serde::de::MapAccess::next_value(&mut __map))))); + } + }) + } else if cattrs.deny_unknown_fields() { None } else { Some(quote! { @@ -2187,7 +2462,7 @@ fn deserialize_map_in_place( }) }; - let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); + let all_skipped = !cattrs.has_flatten() && fields.iter().all(|field| field.attrs.skip_deserializing()); let match_keys = if cattrs.deny_unknown_fields() && all_skipped { quote! { @@ -2251,15 +2526,47 @@ fn deserialize_map_in_place( } }; + let extract_collected = fields_names + .iter() + .filter(|&&(field, _)| field.attrs.flatten()) + .map(|&(field, ref name)| { + let field_ty = field.ty; + quote! { + let #name: #field_ty = try!(_serde::de::Deserialize::deserialize( + _serde::private::de::FlatMapDeserializer( + &mut __collect, + _serde::export::PhantomData))); + } + }); + + let collected_deny_unknown_fields = if cattrs.has_flatten() && cattrs.deny_unknown_fields() { + Some(quote! { + if let _serde::export::Some(_serde::export::Some((__key, _))) = + __collect.into_iter().filter(|x| x.is_some()).next() + { + return _serde::export::Err( + _serde::de::Error::custom(format_args!("unknown field `{}`", &__key))); + } + }) + } else { + None + }; + quote_block! { #(#let_flags)* + #let_collect + #match_keys #let_default #(#check_flags)* + #(#extract_collected)* + + #collected_deny_unknown_fields + _serde::export::Ok(()) } } diff --git a/serde_derive/src/lib.rs b/serde_derive/src/lib.rs index 5cc2e1f8..faf0f1af 100644 --- a/serde_derive/src/lib.rs +++ b/serde_derive/src/lib.rs @@ -26,7 +26,7 @@ #![cfg_attr(feature = "cargo-clippy", allow(enum_variant_names, redundant_field_names, too_many_arguments, used_underscore_binding))] // The `quote!` macro requires deep recursion. -#![recursion_limit = "256"] +#![recursion_limit = "512"] #[macro_use] extern crate quote; diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 8da9e7c3..2b85c125 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -245,6 +245,14 @@ fn serialize_tuple_struct( fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment { assert!(fields.len() as u64 <= u64::from(u32::MAX)); + if cattrs.has_flatten() { + serialize_struct_as_map(params, fields, cattrs) + } else { + serialize_struct_as_struct(params, fields, cattrs) + } +} + +fn serialize_struct_as_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment { let serialize_fields = serialize_struct_visitor( fields, params, @@ -279,6 +287,44 @@ fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Contai } } +fn serialize_struct_as_map(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment { + let serialize_fields = serialize_struct_visitor( + fields, + params, + false, + &StructTrait::SerializeMap, + ); + + let mut serialized_fields = fields + .iter() + .filter(|&field| !field.attrs.skip_serializing()) + .peekable(); + + let let_mut = mut_if(serialized_fields.peek().is_some()); + + let len = if cattrs.has_flatten() { + quote!(_serde::export::None) + } else { + let len = serialized_fields + .map(|field| match field.attrs.skip_serializing_if() { + None => quote!(1), + Some(path) => { + let ident = field.ident.expect("struct has unnamed fields"); + let field_expr = get_member(params, field, &Member::Named(ident)); + quote!(if #path(#field_expr) { 0 } else { 1 }) + } + }) + .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); + quote!(_serde::export::Some(#len)) + }; + + quote_block! { + let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(__serializer, #len)); + #(#serialize_fields)* + _serde::ser::SerializeMap::end(__serde_state) + } +} + fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment { assert!(variants.len() as u64 <= u64::from(u32::MAX)); @@ -859,6 +905,7 @@ fn serialize_struct_visitor( .filter(|&field| !field.attrs.skip_serializing()) .map(|field| { let field_ident = field.ident.expect("struct has unnamed field"); + let mut field_expr = if is_enum { quote!(#field_ident) } else { @@ -877,20 +924,33 @@ fn serialize_struct_visitor( } let span = Span::def_site().located_at(field.original.span()); - let func = struct_trait.serialize_field(span); - let ser = quote! { - try!(#func(&mut __serde_state, #key_expr, #field_expr)); + let ser = if field.attrs.flatten() { + quote! { + try!(_serde::Serialize::serialize(&#field_expr, _serde::private::ser::FlatMapSerializer(&mut __serde_state))); + } + } else { + let func = struct_trait.serialize_field(span); + quote! { + try!(#func(&mut __serde_state, #key_expr, #field_expr)); + } }; match skip { None => ser, Some(skip) => { - let skip_func = struct_trait.skip_field(span); - quote! { - if !#skip { - #ser - } else { - try!(#skip_func(&mut __serde_state, #key_expr)); + if let Some(skip_func) = struct_trait.skip_field(span) { + quote! { + if !#skip { + #ser + } else { + try!(#skip_func(&mut __serde_state, #key_expr)); + } + } + } else { + quote! { + if !#skip { + #ser + } } } } @@ -1011,6 +1071,7 @@ fn get_member(params: &Parameters, field: &Field, member: &Member) -> Tokens { } enum StructTrait { + SerializeMap, SerializeStruct, SerializeStructVariant, } @@ -1018,6 +1079,9 @@ enum StructTrait { impl StructTrait { fn serialize_field(&self, span: Span) -> Tokens { match *self { + StructTrait::SerializeMap => { + quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry) + } StructTrait::SerializeStruct => { quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field) } @@ -1027,14 +1091,15 @@ impl StructTrait { } } - fn skip_field(&self, span: Span) -> Tokens { + fn skip_field(&self, span: Span) -> Option { match *self { - StructTrait::SerializeStruct => { + StructTrait::SerializeMap => None, + StructTrait::SerializeStruct => Some({ quote_spanned!(span=> _serde::ser::SerializeStruct::skip_field) - } - StructTrait::SerializeStructVariant => { + }), + StructTrait::SerializeStructVariant => Some({ quote_spanned!(span=> _serde::ser::SerializeStructVariant::skip_field) - } + }) } } } diff --git a/serde_derive_internals/src/ast.rs b/serde_derive_internals/src/ast.rs index 2ba1f5d9..fd531d8c 100644 --- a/serde_derive_internals/src/ast.rs +++ b/serde_derive_internals/src/ast.rs @@ -48,7 +48,7 @@ pub enum Style { impl<'a> Container<'a> { pub fn from_ast(cx: &Ctxt, item: &'a syn::DeriveInput) -> Container<'a> { - let attrs = attr::Container::from_ast(cx, item); + let mut attrs = attr::Container::from_ast(cx, item); let mut data = match item.data { syn::Data::Enum(ref data) => { @@ -63,6 +63,7 @@ impl<'a> Container<'a> { } }; + let mut has_flatten = false; match data { Data::Enum(ref mut variants) => for variant in variants { variant.attrs.rename_by_rule(attrs.rename_all()); @@ -71,10 +72,17 @@ impl<'a> Container<'a> { } }, Data::Struct(_, ref mut fields) => for field in fields { + if field.attrs.flatten() { + has_flatten = true; + } field.attrs.rename_by_rule(attrs.rename_all()); }, } + if has_flatten { + attrs.mark_has_flatten(); + } + let item = Container { ident: item.ident, attrs: attrs, diff --git a/serde_derive_internals/src/attr.rs b/serde_derive_internals/src/attr.rs index 7c6e3066..96e3a012 100644 --- a/serde_derive_internals/src/attr.rs +++ b/serde_derive_internals/src/attr.rs @@ -27,6 +27,7 @@ use proc_macro2::{Span, TokenStream, TokenNode, TokenTree}; pub use case::RenameRule; +#[derive(Copy, Clone)] struct Attr<'c, T> { cx: &'c Ctxt, name: &'static str, @@ -114,6 +115,7 @@ pub struct Container { type_into: Option, remote: Option, identifier: Identifier, + has_flatten: bool, } /// Styles of representing an enum. @@ -373,6 +375,7 @@ impl Container { type_into: type_into.get(), remote: remote.get(), identifier: decide_identifier(cx, item, &field_identifier, &variant_identifier), + has_flatten: false, } } @@ -419,6 +422,14 @@ impl Container { pub fn identifier(&self) -> Identifier { self.identifier } + + pub fn has_flatten(&self) -> bool { + self.has_flatten + } + + pub fn mark_has_flatten(&mut self) { + self.has_flatten = true; + } } fn decide_tag( @@ -699,6 +710,7 @@ pub struct Field { de_bound: Option>, borrowed_lifetimes: BTreeSet, getter: Option, + flatten: bool, } /// Represents the default to use for a field when deserializing. @@ -732,6 +744,7 @@ impl Field { let mut de_bound = Attr::none(cx, "bound"); let mut borrowed_lifetimes = Attr::none(cx, "borrow"); let mut getter = Attr::none(cx, "getter"); + let mut flatten = BoolAttr::none(cx, "flatten"); let ident = match field.ident { Some(ref ident) => ident.to_string(), @@ -878,6 +891,11 @@ impl Field { } } + // Parse `#[serde(flatten)]` + Meta(Word(word)) if word == "flatten" => { + flatten.set_true(); + } + Meta(ref meta_item) => { cx.error(format!( "unknown serde field attribute `{}`", @@ -970,6 +988,7 @@ impl Field { de_bound: de_bound.get(), borrowed_lifetimes: borrowed_lifetimes, getter: getter.get(), + flatten: flatten.get(), } } @@ -1025,6 +1044,10 @@ impl Field { pub fn getter(&self) -> Option<&syn::ExprPath> { self.getter.as_ref() } + + pub fn flatten(&self) -> bool { + self.flatten + } } type SerAndDe = (Option, Option); diff --git a/serde_derive_internals/src/check.rs b/serde_derive_internals/src/check.rs index c3df861b..dd37085d 100644 --- a/serde_derive_internals/src/check.rs +++ b/serde_derive_internals/src/check.rs @@ -14,6 +14,7 @@ use Ctxt; /// object. Simpler checks should happen when parsing and building the attrs. pub fn check(cx: &Ctxt, cont: &Container) { check_getter(cx, cont); + check_flatten(cx, cont); check_identifier(cx, cont); check_variant_skip_attrs(cx, cont); check_internal_tag_field_name_conflict(cx, cont); @@ -40,6 +41,38 @@ fn check_getter(cx: &Ctxt, cont: &Container) { } } +/// Flattening has some restrictions we can test. +fn check_flatten(cx: &Ctxt, cont: &Container) { + match cont.data { + Data::Enum(_) => { + debug_assert!(!cont.attrs.has_flatten()); + } + Data::Struct(_, _) => { + for field in cont.data.all_fields() { + if !field.attrs.flatten() { + continue; + } + if field.attrs.skip_serializing() { + cx.error( + "#[serde(flatten] can not be combined with \ + #[serde(skip_serializing)]" + ); + } else if field.attrs.skip_serializing_if().is_some() { + cx.error( + "#[serde(flatten] can not be combined with \ + #[serde(skip_serializing_if = \"...\")]" + ); + } else if field.attrs.skip_deserializing() { + cx.error( + "#[serde(flatten] can not be combined with \ + #[serde(skip_deserializing)]" + ); + } + } + } + } +} + /// The `other` attribute must be used at most once and it must be the last /// variant of an enum that has the `field_identifier` attribute. /// diff --git a/test_suite/tests/compile-fail/conflict/flatten-skip-deserializing.rs b/test_suite/tests/compile-fail/conflict/flatten-skip-deserializing.rs new file mode 100644 index 00000000..3ef4d38f --- /dev/null +++ b/test_suite/tests/compile-fail/conflict/flatten-skip-deserializing.rs @@ -0,0 +1,24 @@ +// Copyright 2018 Serde Developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +//~^ HELP: #[serde(flatten] can not be combined with #[serde(skip_deserializing)] +struct Foo { + #[serde(flatten, skip_deserializing)] + other: Other, +} + +#[derive(Deserialize)] +struct Other { + x: u32 +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/conflict/flatten-skip-serializing-if.rs b/test_suite/tests/compile-fail/conflict/flatten-skip-serializing-if.rs new file mode 100644 index 00000000..6a5fa9d6 --- /dev/null +++ b/test_suite/tests/compile-fail/conflict/flatten-skip-serializing-if.rs @@ -0,0 +1,24 @@ +// Copyright 2018 Serde Developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate serde_derive; + +#[derive(Serialize)] //~ ERROR: proc-macro derive panicked +//~^ HELP: #[serde(flatten] can not be combined with #[serde(skip_serializing_if = "...")] +struct Foo { + #[serde(flatten, skip_serializing_if="Option::is_none")] + other: Option, +} + +#[derive(Serialize)] +struct Other { + x: u32 +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/conflict/flatten-skip-serializing.rs b/test_suite/tests/compile-fail/conflict/flatten-skip-serializing.rs new file mode 100644 index 00000000..204f9bf3 --- /dev/null +++ b/test_suite/tests/compile-fail/conflict/flatten-skip-serializing.rs @@ -0,0 +1,24 @@ +// Copyright 2018 Serde Developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate serde_derive; + +#[derive(Serialize)] //~ ERROR: proc-macro derive panicked +//~^ HELP: #[serde(flatten] can not be combined with #[serde(skip_serializing)] +struct Foo { + #[serde(flatten, skip_serializing)] + other: Other, +} + +#[derive(Serialize)] +struct Other { + x: u32 +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/enum-representation/flatten-enum.rs b/test_suite/tests/compile-fail/enum-representation/flatten-enum.rs new file mode 100644 index 00000000..8444860f --- /dev/null +++ b/test_suite/tests/compile-fail/enum-representation/flatten-enum.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Serde Developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate serde_derive; + +#[derive(Serialize)] //~ ERROR: proc-macro derive panicked +//~^ HELP: unknown serde variant attribute `flatten` +enum Foo { + #[serde(flatten)] + Foo { + x: u32, + } +} + +fn main() {} diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index 48a09a1a..6ce2c3ef 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -12,12 +12,13 @@ extern crate serde_derive; extern crate serde; +use std::collections::HashMap; use self::serde::{Deserialize, Deserializer, Serialize, Serializer}; use self::serde::de::{self, Unexpected}; extern crate serde_test; -use self::serde_test::{assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, assert_tokens, - Token}; +use self::serde_test::{assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, + assert_ser_tokens_error, assert_tokens, Token}; trait MyDefault: Sized { fn my_default() -> Self; @@ -94,6 +95,56 @@ where a5: E, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct CollectOther { + a: u32, + b: u32, + #[serde(flatten)] + extra: HashMap, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct FlattenStructEnumWrapper { + #[serde(flatten)] + data: FlattenStructEnum, + #[serde(flatten)] + extra: HashMap, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +enum FlattenStructEnum { + InsertInteger { + index: u32, + value: u32 + }, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct FlattenStructTagContentEnumWrapper { + outer: u32, + #[serde(flatten)] + data: FlattenStructTagContentEnumNewtype, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct FlattenStructTagContentEnumNewtype(pub FlattenStructTagContentEnum); + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case", tag = "type", content = "value")] +enum FlattenStructTagContentEnum { + InsertInteger { + index: u32, + value: u32 + }, + NewtypeVariant(FlattenStructTagContentEnumNewtypeVariant), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct FlattenStructTagContentEnumNewtypeVariant { + value: u32, +} + #[test] fn test_default_struct() { assert_de_tokens( @@ -1267,3 +1318,482 @@ fn test_from_into_traits() { assert_ser_tokens::(&StructFromEnum(None), &[Token::None]); assert_de_tokens::(&StructFromEnum(Some(2)), &[Token::Some, Token::U32(2)]); } + +#[test] +fn test_collect_other() { + let mut extra = HashMap::new(); + extra.insert("c".into(), 3); + assert_tokens( + &CollectOther { a: 1, b: 2, extra }, + &[ + Token::Map { len: None }, + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::U32(2), + Token::Str("c"), + Token::U32(3), + Token::MapEnd, + ], + ); +} + +#[test] +fn test_flatten_struct_enum() { + let mut extra = HashMap::new(); + extra.insert("extra_key".into(), "extra value".into()); + let change_request = FlattenStructEnumWrapper { + data: FlattenStructEnum::InsertInteger { + index: 0, + value: 42 + }, + 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::Str("extra value"), + 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::Str("extra value"), + Token::MapEnd + ], + ); +} + +#[test] +fn test_flatten_struct_tag_content_enum() { + let change_request = FlattenStructTagContentEnumWrapper { + outer: 42, + data: FlattenStructTagContentEnumNewtype( + FlattenStructTagContentEnum::InsertInteger { + index: 0, + value: 42 + } + ), + }; + assert_de_tokens( + &change_request, + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::U32(42), + Token::Str("type"), + Token::Str("insert_integer"), + Token::Str("value"), + Token::Map { len: None }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::MapEnd, + Token::MapEnd, + ], + ); + assert_ser_tokens( + &change_request, + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::U32(42), + Token::Str("type"), + Token::Str("insert_integer"), + Token::Str("value"), + Token::Struct { len: 2, name: "insert_integer" }, + Token::Str("index"), + Token::U32(0), + Token::Str("value"), + Token::U32(42), + Token::StructEnd, + Token::MapEnd, + ], + ); +} + +#[test] +fn test_flatten_struct_tag_content_enum_newtype() { + let change_request = FlattenStructTagContentEnumWrapper { + outer: 42, + data: FlattenStructTagContentEnumNewtype( + FlattenStructTagContentEnum::NewtypeVariant( + FlattenStructTagContentEnumNewtypeVariant { + value: 23 + } + ) + ), + }; + assert_de_tokens( + &change_request, + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::U32(42), + Token::Str("type"), + Token::Str("newtype_variant"), + Token::Str("value"), + Token::Map { len: None }, + Token::Str("value"), + Token::U32(23), + Token::MapEnd, + Token::MapEnd, + ], + ); + assert_ser_tokens( + &change_request, + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::U32(42), + Token::Str("type"), + Token::Str("newtype_variant"), + Token::Str("value"), + Token::Struct { len: 1, name: "FlattenStructTagContentEnumNewtypeVariant" }, + Token::Str("value"), + Token::U32(23), + Token::StructEnd, + Token::MapEnd, + ], + ); +} + +#[test] +fn test_unknown_field_in_flatten() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(deny_unknown_fields)] + struct Outer { + dummy: String, + #[serde(flatten)] + inner: Inner, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Inner { + foo: HashMap, + } + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "Outer", + len: 1, + }, + Token::Str("dummy"), + Token::Str("23"), + Token::Str("foo"), + Token::Map { len: None }, + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::U32(2), + Token::MapEnd, + Token::Str("bar"), + Token::U32(23), + Token::StructEnd, + ], + "unknown field `bar`", + ); +} + +#[test] +fn test_complex_flatten() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Outer { + y: u32, + #[serde(flatten)] + first: First, + #[serde(flatten)] + second: Second, + z: u32 + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct First { + a: u32, + b: bool, + c: Vec, + d: String, + e: Option, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Second { + f: u32, + } + + assert_de_tokens( + &Outer { + y: 0, + first: First { + a: 1, + b: true, + c: vec!["a".into(), "b".into()], + d: "c".into(), + e: Some(2), + }, + second: Second { + f: 3 + }, + z: 4 + }, + &[ + Token::Map { len: None }, + Token::Str("y"), + Token::U32(0), + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::Bool(true), + Token::Str("c"), + Token::Seq { len: Some(2) }, + Token::Str("a"), + Token::Str("b"), + Token::SeqEnd, + Token::Str("d"), + Token::Str("c"), + Token::Str("e"), + Token::U64(2), + Token::Str("f"), + Token::U32(3), + Token::Str("z"), + Token::U32(4), + Token::MapEnd, + ], + ); + + assert_ser_tokens( + &Outer { + y: 0, + first: First { + a: 1, + b: true, + c: vec!["a".into(), "b".into()], + d: "c".into(), + e: Some(2), + }, + second: Second { + f: 3 + }, + z: 4 + }, + &[ + Token::Map { len: None }, + Token::Str("y"), + Token::U32(0), + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::Bool(true), + Token::Str("c"), + Token::Seq { len: Some(2) }, + Token::Str("a"), + Token::Str("b"), + Token::SeqEnd, + Token::Str("d"), + Token::Str("c"), + Token::Str("e"), + Token::Some, + Token::U64(2), + Token::Str("f"), + Token::U32(3), + Token::Str("z"), + Token::U32(4), + Token::MapEnd, + ], + ); +} + +#[test] +fn test_flatten_unsupported_type() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Outer { + outer: String, + #[serde(flatten)] + inner: String, + } + + assert_ser_tokens_error( + &Outer { + outer: "foo".into(), + inner: "bar".into(), + }, + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::Str("foo"), + ], + "can only flatten structs and maps (got a string)", + ); + assert_de_tokens_error::( + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::Str("foo"), + Token::Str("a"), + Token::Str("b"), + Token::MapEnd + ], + "can only flatten structs and maps", + ); +} + +#[test] +fn test_non_string_keys() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct TestStruct { + name: String, + age: u32, + #[serde(flatten)] + mapping: HashMap, + } + + let mut mapping = HashMap::new(); + mapping.insert(0, 42); + assert_tokens( + &TestStruct { name: "peter".into(), age: 3, mapping }, + &[ + Token::Map { len: None }, + Token::Str("name"), + Token::Str("peter"), + Token::Str("age"), + Token::U32(3), + Token::U32(0), + Token::U32(42), + Token::MapEnd, + ], + ); +} + +#[test] +fn test_lifetime_propagation_for_flatten() { + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde(flatten)] + t: T, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B<'a> { + #[serde(flatten, borrow)] + t: HashMap<&'a str, u32>, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct C<'a> { + #[serde(flatten, borrow)] + t: HashMap<&'a [u8], u32>, + } + + let mut owned_map = HashMap::new(); + owned_map.insert("x".to_string(), 42u32); + assert_tokens( + &A { t: owned_map }, + &[ + Token::Map { len: None }, + Token::Str("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + let mut borrowed_map = HashMap::new(); + borrowed_map.insert("x", 42u32); + assert_ser_tokens( + &B { t: borrowed_map.clone() }, + &[ + Token::Map { len: None }, + Token::BorrowedStr("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &B { t: borrowed_map }, + &[ + Token::Map { len: None }, + Token::BorrowedStr("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + let mut borrowed_map = HashMap::new(); + borrowed_map.insert(&b"x"[..], 42u32); + assert_ser_tokens( + &C { t: borrowed_map.clone() }, + &[ + Token::Map { len: None }, + Token::Seq { len: Some(1) }, + Token::U8(120), + Token::SeqEnd, + Token::U32(42), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &C { t: borrowed_map }, + &[ + Token::Map { len: None }, + Token::BorrowedBytes(b"x"), + Token::U32(42), + Token::MapEnd, + ], + ); +} + +#[test] +fn test_flatten_enum_newtype() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct S { + #[serde(flatten)] + flat: E, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + enum E { + Q(HashMap), + } + + let e = E::Q({ + let mut map = HashMap::new(); + map.insert("k".to_owned(), "v".to_owned()); + map + }); + let s = S { flat: e }; + + assert_tokens( + &s, + &[ + Token::Map { len: None }, + Token::Str("Q"), + Token::Map { len: Some(1) }, + Token::Str("k"), + Token::Str("v"), + Token::MapEnd, + Token::MapEnd, + ], + ); +}