diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 8da9e7c3..83720fea 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -245,6 +245,13 @@ fn serialize_tuple_struct( fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment { assert!(fields.len() as u64 <= u64::from(u32::MAX)); + match cattrs.repr() { + attr::ContainerRepr::Struct | attr::ContainerRepr::Auto => serialize_struct_as_struct(params, fields, cattrs), + attr::ContainerRepr::Map => serialize_struct_as_map(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 +286,64 @@ 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 mut collect_extra = None; + if cattrs.unknown_fields_into().is_some() { + if let Some(field) = fields + .iter() + .filter(|field| field.attrs.collection_field()) + .next() + { + let ident = &field.ident; + collect_extra = Some(quote! { + for (ref __key, ref __value) in &self.#ident { + try!(_serde::ser::SerializeMap::serialize_entry( + &mut __serde_state, + __key, + __value)); + } + }); + } + } + + let let_mut = mut_if(serialized_fields.peek().is_some()); + + let len = if collect_extra.is_some() { + quote!(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!(Some(#len)) + }; + + quote_block! { + let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(__serializer, #len)); + #(#serialize_fields)* + #collect_extra + _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)); @@ -885,12 +950,19 @@ fn serialize_struct_visitor( 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 +1083,7 @@ fn get_member(params: &Parameters, field: &Field, member: &Member) -> Tokens { } enum StructTrait { + SerializeMap, SerializeStruct, SerializeStructVariant, } @@ -1018,6 +1091,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 +1103,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) - } + }) } } }