From 1d6ef76cfb339df48232b59cc2ce8568fd19660c Mon Sep 17 00:00:00 2001 From: Johannes Willbold Date: Sun, 3 Feb 2019 02:09:37 +0100 Subject: [PATCH] Fixed #1468, flattened struct fields made structs ignore their tag --- serde_derive/src/ser.rs | 50 ++++++++++++++++++++------------- test_suite/tests/test_macros.rs | 42 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 1dacdc05..e87ddee8 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -289,37 +289,41 @@ fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Contai } } +fn serialize_struct_tag_field( + cattrs: &attr::Container, + struct_trait: &StructTrait, +) -> TokenStream { + match *cattrs.tag() { + attr::TagType::Internal { ref tag } => { + let type_name = cattrs.name().serialize_name(); + let func = struct_trait.serialize_field(Span::call_site()); + quote! { + try!(#func(&mut __serde_state, #tag, #type_name)); + } + } + _ => quote!{} + } +} + fn serialize_struct_as_struct( params: &Parameters, fields: &[Field], cattrs: &attr::Container, ) -> Fragment { - let mut serialize_fields = + let serialize_fields = serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct); let type_name = cattrs.name().serialize_name(); - let additional_field_count: usize = match *cattrs.tag() { - attr::TagType::Internal { ref tag } => { - let func = StructTrait::SerializeStruct.serialize_field(Span::call_site()); - serialize_fields.insert( - 0, - quote! { - try!(#func(&mut __serde_state, #tag, #type_name)); - }, - ); - - 1 - } - _ => 0, - }; + let tag_field = serialize_struct_tag_field(cattrs, &StructTrait::SerializeStruct); + let tag_field_exists = !tag_field.is_empty(); let mut serialized_fields = fields .iter() .filter(|&field| !field.attrs.skip_serializing()) .peekable(); - let let_mut = mut_if(serialized_fields.peek().is_some() || additional_field_count > 0); + let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists); let len = serialized_fields .map(|field| match field.attrs.skip_serializing_if() { @@ -330,12 +334,13 @@ fn serialize_struct_as_struct( } }) .fold( - quote!(#additional_field_count), + quote!(#tag_field_exists as usize), |sum, expr| quote!(#sum + #expr), ); quote_block! { let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len)); + #tag_field #(#serialize_fields)* _serde::ser::SerializeStruct::end(__serde_state) } @@ -349,12 +354,15 @@ fn serialize_struct_as_map( let serialize_fields = serialize_struct_visitor(fields, params, false, &StructTrait::SerializeMap); + let tag_field = serialize_struct_tag_field(cattrs, &StructTrait::SerializeMap); + let tag_field_exists = !tag_field.is_empty(); + let mut serialized_fields = fields .iter() .filter(|&field| !field.attrs.skip_serializing()) .peekable(); - let let_mut = mut_if(serialized_fields.peek().is_some()); + let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists); let len = if cattrs.has_flatten() { quote!(_serde::export::None) @@ -367,12 +375,16 @@ fn serialize_struct_as_map( quote!(if #path(#field_expr) { 0 } else { 1 }) } }) - .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); + .fold( + quote!(#tag_field_exists as usize), + |sum, expr| quote!(#sum + #expr) + ); quote!(_serde::export::Some(#len)) }; quote_block! { let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(__serializer, #len)); + #tag_field #(#serialize_fields)* _serde::ser::SerializeMap::end(__serde_state) } diff --git a/test_suite/tests/test_macros.rs b/test_suite/tests/test_macros.rs index 280f0043..dd959137 100644 --- a/test_suite/tests/test_macros.rs +++ b/test_suite/tests/test_macros.rs @@ -1424,6 +1424,48 @@ fn test_internally_tagged_braced_struct_with_zero_fields() { ); } +#[test] +fn test_internally_tagged_struct_with_flattened_field() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag="tag_struct")] + pub struct Struct { + #[serde(flatten)] + pub flat: Enum + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag="tag_enum", content="content")] + pub enum Enum { + A(u64), + } + + assert_tokens( + &Struct{flat: Enum::A(0)}, + &[ + Token::Map { len: None }, + Token::Str("tag_struct"), + Token::Str("Struct"), + Token::Str("tag_enum"), + Token::Str("A"), + Token::Str("content"), + Token::U64(0), + Token::MapEnd + ] + ); + + assert_de_tokens( + &Struct{flat: Enum::A(0)}, + &[ + Token::Map { len: None }, + Token::Str("tag_enum"), + Token::Str("A"), + Token::Str("content"), + Token::U64(0), + Token::MapEnd + ] + ); +} + #[test] fn test_enum_in_untagged_enum() { #[derive(Debug, PartialEq, Serialize, Deserialize)]