Skip to content

Commit

Permalink
Merge pull request #1474 from jwillbold/master
Browse files Browse the repository at this point in the history
Fixed #1468, flattened struct fields made structs ignore their tag
  • Loading branch information
dtolnay committed Feb 3, 2019
2 parents c8e3959 + 1d6ef76 commit 6a3a820
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 19 deletions.
50 changes: 31 additions & 19 deletions serde_derive/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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)
}
Expand All @@ -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)
Expand All @@ -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)
}
Expand Down
42 changes: 42 additions & 0 deletions test_suite/tests/test_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down

0 comments on commit 6a3a820

Please sign in to comment.