diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index d8dc7379..cc5cfe38 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -177,31 +177,29 @@ pub struct FieldAttrs { name: Name, skip_serializing_field: bool, skip_deserializing_field: bool, - skip_serializing_field_if: Option>, + skip_serializing_if: Option, default_expr_if_missing: Option>, - serialize_with: Option>, + serialize_with: Option, deserialize_with: Option, } impl FieldAttrs { /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn from_field(cx: &ExtCtxt, - container_ty: &P, - generics: &ast::Generics, - field: &ast::StructField, - is_enum: bool) -> Result { + index: usize, + field: &ast::StructField) -> Result { let builder = AstBuilder::new(); let field_ident = match field.ident { Some(ident) => ident, - None => { cx.span_bug(field.span, "struct field has no name?") } + None => builder.id(index.to_string()), }; let mut field_attrs = FieldAttrs { name: Name::new(field_ident), skip_serializing_field: false, skip_deserializing_field: false, - skip_serializing_field_if: None, + skip_serializing_if: None, default_expr_if_missing: None, serialize_with: None, deserialize_with: None, @@ -260,27 +258,14 @@ impl FieldAttrs { // Parse `#[serde(skip_serializing_if="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => { - let expr = wrap_skip_serializing( - field_ident, - try!(parse_lit_into_path(cx, name, lit)), - is_enum, - ); - - field_attrs.skip_serializing_field_if = Some(expr); + let path = try!(parse_lit_into_path(cx, name, lit)); + field_attrs.skip_serializing_if = Some(path); } // Parse `#[serde(serialize_with="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => { - let expr = wrap_serialize_with( - cx, - container_ty, - generics, - field_ident, - try!(parse_lit_into_path(cx, name, lit)), - is_enum, - ); - - field_attrs.serialize_with = Some(expr); + let path = try!(parse_lit_into_path(cx, name, lit)); + field_attrs.serialize_with = Some(path); } // Parse `#[serde(deserialize_with="...")]` @@ -316,15 +301,15 @@ impl FieldAttrs { self.skip_deserializing_field } - pub fn skip_serializing_field_if(&self) -> Option<&P> { - self.skip_serializing_field_if.as_ref() + pub fn skip_serializing_if(&self) -> Option<&ast::Path> { + self.skip_serializing_if.as_ref() } pub fn default_expr_if_missing(&self) -> Option<&P> { self.default_expr_if_missing.as_ref() } - pub fn serialize_with(&self) -> Option<&P> { + pub fn serialize_with(&self) -> Option<&ast::Path> { self.serialize_with.as_ref() } @@ -334,14 +319,17 @@ impl FieldAttrs { } -/// Extract out the `#[serde(...)]` attributes from a struct field. -pub fn get_struct_field_attrs(cx: &ExtCtxt, - container_ty: &P, - generics: &ast::Generics, - fields: &[ast::StructField], - is_enum: bool) -> Result, Error> { +/// Zip together fields and `#[serde(...)]` attributes on those fields. +pub fn fields_with_attrs<'a>( + cx: &ExtCtxt, + fields: &'a [ast::StructField], +) -> Result, Error> { fields.iter() - .map(|field| FieldAttrs::from_field(cx, container_ty, generics, field, is_enum)) + .enumerate() + .map(|(i, field)| { + let attrs = try!(FieldAttrs::from_field(cx, i, field)); + Ok((field, attrs)) + }) .collect() } @@ -495,100 +483,3 @@ fn wrap_default(path: ast::Path) -> P { .build_path(path) .build() } - -/// This function wraps the expression in `#[serde(skip_serializing_if="...")]` in a trait to -/// prevent it from accessing the internal `Serialize` state. -fn wrap_skip_serializing(field_ident: ast::Ident, - path: ast::Path, - is_enum: bool) -> P { - let builder = AstBuilder::new(); - - let expr = builder.expr() - .field(field_ident) - .field("value") - .self_(); - - let expr = if is_enum { - expr - } else { - builder.expr().ref_().build(expr) - }; - - builder.expr().call() - .build_path(path) - .arg().build(expr) - .build() -} - -/// This function wraps the expression in `#[serde(serialize_with="...")]` in a trait to -/// prevent it from accessing the internal `Serialize` state. -fn wrap_serialize_with(cx: &ExtCtxt, - container_ty: &P, - generics: &ast::Generics, - field_ident: ast::Ident, - path: ast::Path, - is_enum: bool) -> P { - let builder = AstBuilder::new(); - - let expr = builder.expr() - .field(field_ident) - .self_(); - - let expr = if is_enum { - expr - } else { - builder.expr().ref_().build(expr) - }; - - let expr = builder.expr().call() - .build_path(path) - .arg().build(expr) - .arg() - .id("serializer") - .build(); - - let where_clause = &generics.where_clause; - - quote_expr!(cx, { - trait __SerdeSerializeWith { - fn __serde_serialize_with<__S>(&self, serializer: &mut __S) -> Result<(), __S::Error> - where __S: _serde::ser::Serializer; - } - - impl<'__a, __T> __SerdeSerializeWith for &'__a __T - where __T: '__a + __SerdeSerializeWith, - { - fn __serde_serialize_with<__S>(&self, serializer: &mut __S) -> Result<(), __S::Error> - where __S: _serde::ser::Serializer - { - (**self).__serde_serialize_with(serializer) - } - } - - impl $generics __SerdeSerializeWith for $container_ty $where_clause { - fn __serde_serialize_with<__S>(&self, serializer: &mut __S) -> Result<(), __S::Error> - where __S: _serde::ser::Serializer - { - $expr - } - } - - struct __SerdeSerializeWithStruct<'__a, __T: '__a> { - value: &'__a __T, - } - - impl<'__a, __T> _serde::ser::Serialize for __SerdeSerializeWithStruct<'__a, __T> - where __T: '__a + __SerdeSerializeWith - { - fn serialize<__S>(&self, serializer: &mut __S) -> Result<(), __S::Error> - where __S: _serde::ser::Serializer - { - self.value.__serde_serialize_with(serializer) - } - } - - __SerdeSerializeWithStruct { - value: &self.value, - } - }) -} diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 5573e385..a2779709 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -214,7 +214,7 @@ fn deserialize_item_struct( None, impl_generics, ty, - fields.len(), + fields, container_attrs, ) } @@ -358,7 +358,7 @@ fn deserialize_tuple( variant_ident: Option, impl_generics: &ast::Generics, ty: P, - fields: usize, + fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let where_clause = &impl_generics.where_clause; @@ -376,37 +376,43 @@ fn deserialize_tuple( None => builder.path().id(type_ident).build(), }; - let visit_newtype_struct = if !is_enum && fields == 1 { - Some(quote_tokens!(cx, - #[inline] - fn visit_newtype_struct<__E>(&mut self, deserializer: &mut __E) -> ::std::result::Result - where __E: _serde::de::Deserializer, - { - let value = try!(_serde::de::Deserialize::deserialize(deserializer)); - Ok($type_path(value)) - })) + let nfields = fields.len(); + let fields_with_attrs = try!(attr::fields_with_attrs(cx, fields)); + + let visit_newtype_struct = if !is_enum && nfields == 1 { + Some(try!(deserialize_newtype_struct( + cx, + builder, + type_ident, + &type_path, + impl_generics, + &fields_with_attrs[0], + ))) } else { None }; - let visit_seq_expr = deserialize_seq( + let visit_seq_expr = try!(deserialize_seq( cx, builder, + type_ident, type_path, - fields, - ); + impl_generics, + &fields_with_attrs, + false, + )); let dispatch = if is_enum { quote_expr!(cx, - visitor.visit_tuple($fields, $visitor_expr)) - } else if fields == 1 { + visitor.visit_tuple($nfields, $visitor_expr)) + } else if nfields == 1 { let type_name = container_attrs.name().deserialize_name_expr(); quote_expr!(cx, deserializer.deserialize_newtype_struct($type_name, $visitor_expr)) } else { let type_name = container_attrs.name().deserialize_name_expr(); quote_expr!(cx, - deserializer.deserialize_tuple_struct($type_name, $fields, $visitor_expr)) + deserializer.deserialize_tuple_struct($type_name, $nfields, $visitor_expr)) }; Ok(quote_expr!(cx, { @@ -430,46 +436,13 @@ fn deserialize_tuple( } fn deserialize_seq( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - struct_path: ast::Path, - fields: usize, -) -> P { - let let_values: Vec = (0 .. fields) - .map(|i| { - let name = builder.id(format!("__field{}", i)); - quote_stmt!(cx, - let $name = match try!(visitor.visit()) { - Some(value) => { value }, - None => { - return Err(_serde::de::Error::end_of_stream()); - } - }; - ).unwrap() - }) - .collect(); - - let result = builder.expr().call() - .build_path(struct_path) - .with_args((0 .. fields).map(|i| builder.expr().id(format!("__field{}", i)))) - .build(); - - quote_expr!(cx, { - $let_values - - try!(visitor.end()); - - Ok($result) - }) -} - -fn deserialize_struct_as_seq( cx: &ExtCtxt, builder: &aster::AstBuilder, type_ident: Ident, - struct_path: ast::Path, + type_path: ast::Path, impl_generics: &ast::Generics, fields: &[(&ast::StructField, attr::FieldAttrs)], + is_struct: bool, ) -> Result, Error> { let let_values: Vec<_> = fields.iter() .enumerate() @@ -508,23 +481,30 @@ fn deserialize_struct_as_seq( }) .collect(); - let result = builder.expr().struct_path(struct_path) - .with_id_exprs( - fields.iter() - .enumerate() - .map(|(i, &(field, _))| { - ( - match field.ident { - Some(name) => name.clone(), - None => { - cx.span_bug(field.span, "struct contains unnamed fields") - } - }, - builder.expr().id(format!("__field{}", i)), - ) - }) - ) - .build(); + let result = if is_struct { + builder.expr().struct_path(type_path) + .with_id_exprs( + fields.iter() + .enumerate() + .map(|(i, &(field, _))| { + ( + match field.ident { + Some(name) => name.clone(), + None => { + cx.span_bug(field.span, "struct contains unnamed fields") + } + }, + builder.expr().id(format!("__field{}", i)), + ) + }) + ) + .build() + } else { + builder.expr().call() + .build_path(type_path) + .with_args((0..fields.len()).map(|i| builder.expr().id(format!("__field{}", i)))) + .build() + }; Ok(quote_expr!(cx, { $let_values @@ -535,6 +515,41 @@ fn deserialize_struct_as_seq( })) } +fn deserialize_newtype_struct( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + type_ident: Ident, + type_path: &ast::Path, + impl_generics: &ast::Generics, + field: &(&ast::StructField, attr::FieldAttrs), +) -> Result, Error> { + let &(field, ref attrs) = field; + let value = match attrs.deserialize_with() { + None => { + let field_ty = &field.ty; + quote_expr!(cx, + try!(<$field_ty as _serde::Deserialize>::deserialize(__e))) + } + Some(path) => { + let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( + cx, builder, type_ident, impl_generics, &field.ty, path); + quote_expr!(cx, { + $wrapper + $wrapper_impl + try!(<$wrapper_ty as _serde::Deserialize>::deserialize(__e)).value + }) + } + }; + Ok(quote_tokens!(cx, + #[inline] + fn visit_newtype_struct<__E>(&mut self, __e: &mut __E) -> ::std::result::Result + where __E: _serde::de::Deserializer, + { + Ok($type_path($value)) + } + )) +} + fn deserialize_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, @@ -559,16 +574,16 @@ fn deserialize_struct( None => builder.path().id(type_ident).build(), }; - let is_enum = variant_ident.is_some(); - let fields_with_attrs = try!(fields_with_attrs(cx, impl_generics, &ty, fields, is_enum)); + let fields_with_attrs = try!(attr::fields_with_attrs(cx, fields)); - let visit_seq_expr = try!(deserialize_struct_as_seq( + let visit_seq_expr = try!(deserialize_seq( cx, builder, type_ident, type_path.clone(), impl_generics, &fields_with_attrs, + true, )); let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor( @@ -581,6 +596,7 @@ fn deserialize_struct( container_attrs, )); + let is_enum = variant_ident.is_some(); let dispatch = if is_enum { quote_expr!(cx, visitor.visit_struct(FIELDS, $visitor_expr)) @@ -737,10 +753,14 @@ fn deserialize_variant( })) } ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { - Ok(quote_expr!(cx, { - let val = try!(visitor.visit_newtype()); - Ok($type_ident::$variant_ident(val)) - })) + deserialize_newtype_variant( + cx, + builder, + type_ident, + variant_ident, + generics, + &fields[0], + ) } ast::VariantData::Tuple(ref fields, _) => { deserialize_tuple( @@ -750,7 +770,7 @@ fn deserialize_variant( Some(variant_ident), generics, ty, - fields.len(), + fields, container_attrs, ) } @@ -769,6 +789,33 @@ fn deserialize_variant( } } +fn deserialize_newtype_variant( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + type_ident: Ident, + variant_ident: Ident, + impl_generics: &ast::Generics, + field: &ast::StructField, +) -> Result, Error> { + let attrs = try!(attr::FieldAttrs::from_field(cx, 0, field)); + let visit = match attrs.deserialize_with() { + None => { + let field_ty = &field.ty; + quote_expr!(cx, try!(visitor.visit_newtype::<$field_ty>())) + } + Some(path) => { + let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( + cx, builder, type_ident, impl_generics, &field.ty, path); + quote_expr!(cx, { + $wrapper + $wrapper_impl + try!(visitor.visit_newtype::<$wrapper_ty>()).value + }) + } + }; + Ok(quote_expr!(cx, Ok($type_ident::$variant_ident($visit)))) +} + fn deserialize_field_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, @@ -1117,21 +1164,6 @@ fn deserialize_map( })) } -fn fields_with_attrs<'a>( - cx: &ExtCtxt, - generics: &ast::Generics, - ty: &P, - fields: &'a [ast::StructField], - is_enum: bool -) -> Result, Error> { - fields.iter() - .map(|field| { - let attrs = try!(attr::FieldAttrs::from_field(cx, &ty, generics, field, is_enum)); - Ok((field, attrs)) - }) - .collect() -} - /// This function wraps the expression in `#[serde(deserialize_with="...")]` in /// a trait to prevent it from accessing the internal `Deserialize` state. fn wrap_deserialize_with( diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index dcae12d0..a1220f69 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -185,6 +185,10 @@ fn serialize_item_struct( ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { serialize_newtype_struct( cx, + &builder, + impl_generics, + ty, + &fields[0], container_attrs, ) } @@ -198,7 +202,7 @@ fn serialize_item_struct( &builder, impl_generics, ty, - fields.len(), + fields, container_attrs, ) } @@ -232,12 +236,24 @@ fn serialize_unit_struct( fn serialize_newtype_struct( cx: &ExtCtxt, + builder: &aster::AstBuilder, + impl_generics: &ast::Generics, + container_ty: P, + field: &ast::StructField, container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let type_name = container_attrs.name().serialize_name_expr(); + let attrs = try!(attr::FieldAttrs::from_field(cx, 0, field)); + + let mut field_expr = quote_expr!(cx, &self.0); + if let Some(path) = attrs.serialize_with() { + field_expr = wrap_serialize_with(cx, builder, + &container_ty, impl_generics, &field.ty, path, field_expr); + } + Ok(quote_expr!(cx, - _serializer.serialize_newtype_struct($type_name, &self.0) + _serializer.serialize_newtype_struct($type_name, $field_expr) )) } @@ -246,10 +262,10 @@ fn serialize_tuple_struct( builder: &aster::AstBuilder, impl_generics: &ast::Generics, ty: P, - fields: usize, + fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( + let (visitor_struct, visitor_impl) = try!(serialize_tuple_struct_visitor( cx, builder, ty.clone(), @@ -260,7 +276,8 @@ fn serialize_tuple_struct( builder.id("serialize_tuple_struct_elt"), fields, impl_generics, - ); + false, + )); let type_name = container_attrs.name().serialize_name_expr(); @@ -362,12 +379,8 @@ fn serialize_variant( match variant.node.data { ast::VariantData::Unit(_) => { - let pat = builder.pat().path() - .id(type_ident).id(variant_ident) - .build(); - Ok(quote_arm!(cx, - $pat => { + $type_ident::$variant_ident => { _serde::ser::Serializer::serialize_unit_variant( _serializer, $type_name, @@ -378,23 +391,19 @@ fn serialize_variant( )) }, ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { - let field = builder.id("__simple_value"); - let field = builder.pat().ref_id(field); - let pat = builder.pat().enum_() - .id(type_ident).id(variant_ident).build() - .with_pats(Some(field).into_iter()) - .build(); + let expr = try!(serialize_newtype_variant( + cx, + builder, + type_name, + variant_index, + variant_name, + ty, + generics, + &fields[0], + )); Ok(quote_arm!(cx, - $pat => { - _serde::ser::Serializer::serialize_newtype_variant( - _serializer, - $type_name, - $variant_index, - $variant_name, - __simple_value, - ) - } + $type_ident::$variant_ident(ref __simple_value) => { $expr } )) }, ast::VariantData::Tuple(ref fields, _) => { @@ -410,7 +419,7 @@ fn serialize_variant( ) .build(); - let expr = serialize_tuple_variant( + let expr = try!(serialize_tuple_variant( cx, builder, type_name, @@ -420,7 +429,7 @@ fn serialize_variant( ty, fields, field_names, - ); + )); Ok(quote_arm!(cx, $pat => { $expr } @@ -468,6 +477,35 @@ fn serialize_variant( } } +fn serialize_newtype_variant( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + type_name: P, + variant_index: usize, + variant_name: P, + container_ty: P, + generics: &ast::Generics, + field: &ast::StructField, +) -> Result, Error> { + let attrs = try!(attr::FieldAttrs::from_field(cx, 0, field)); + + let mut field_expr = quote_expr!(cx, __simple_value); + if let Some(path) = attrs.serialize_with() { + field_expr = wrap_serialize_with(cx, builder, + &container_ty, generics, &field.ty, path, field_expr); + } + + Ok(quote_expr!(cx, + _serde::ser::Serializer::serialize_newtype_variant( + _serializer, + $type_name, + $variant_index, + $variant_name, + $field_expr, + ) + )) +} + fn serialize_tuple_variant( cx: &ExtCtxt, builder: &aster::AstBuilder, @@ -478,7 +516,7 @@ fn serialize_tuple_variant( structure_ty: P, fields: &[ast::StructField], field_names: Vec, -) -> P { +) -> Result, Error> { let variant_ty = builder.ty().tuple() .with_tys( fields.iter().map(|field| { @@ -490,15 +528,16 @@ fn serialize_tuple_variant( ) .build(); - let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( + let (visitor_struct, visitor_impl) = try!(serialize_tuple_struct_visitor( cx, builder, structure_ty.clone(), variant_ty, builder.id("serialize_tuple_variant_elt"), - fields.len(), + fields, generics, - ); + true, + )); let value_expr = builder.expr().tuple() .with_exprs( @@ -508,7 +547,7 @@ fn serialize_tuple_variant( ) .build(); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $visitor_struct $visitor_impl _serializer.serialize_tuple_variant($type_name, $variant_index, $variant_name, Visitor { @@ -516,7 +555,7 @@ fn serialize_tuple_variant( state: 0, _structure_ty: ::std::marker::PhantomData::<&$structure_ty>, }) - }) + })) } fn serialize_struct_variant( @@ -614,20 +653,33 @@ fn serialize_tuple_struct_visitor( structure_ty: P, variant_ty: P, serializer_method: ast::Ident, - fields: usize, - generics: &ast::Generics -) -> (P, P) { - let arms: Vec = (0 .. fields) - .map(|i| { - let expr = builder.expr().method_call(serializer_method) - .id("_serializer") - .arg().ref_().tup_field(i).field("value").self_() - .build(); + fields: &[ast::StructField], + generics: &ast::Generics, + is_enum: bool, +) -> Result<(P, P), Error> { + let fields_with_attrs = try!(attr::fields_with_attrs(cx, fields)); + + let arms: Vec<_> = fields_with_attrs.iter() + .enumerate() + .map(|(i, &(field, ref attrs))| { + let mut field_expr = builder.expr().tup_field(i).field("value").self_(); + if !is_enum { + field_expr = quote_expr!(cx, &$field_expr); + } + + let continue_if_skip = attrs.skip_serializing_if() + .map(|path| quote_stmt!(cx, if $path($field_expr) { continue })); + + if let Some(path) = attrs.serialize_with() { + field_expr = wrap_serialize_with(cx, builder, + &structure_ty, generics, &field.ty, path, field_expr); + } quote_arm!(cx, $i => { self.state += 1; - Ok(Some(try!($expr))) + $continue_if_skip + Ok(Some(try!(_serializer.$serializer_method($field_expr)))) } ) }) @@ -644,7 +696,9 @@ fn serialize_tuple_struct_visitor( .strip_bounds() .build(); - ( + let nfields = fields.len(); + + Ok(( quote_item!(cx, struct Visitor $visitor_impl_generics $where_clause { state: usize, @@ -669,11 +723,11 @@ fn serialize_tuple_struct_visitor( #[inline] fn len(&self) -> Option { - Some($fields) + Some($nfields) } } ).unwrap(), - ) + )) } fn serialize_struct_visitor( @@ -686,28 +740,27 @@ fn serialize_struct_visitor( generics: &ast::Generics, is_enum: bool, ) -> Result<(P, P), Error> { - let field_attrs = try!( - attr::get_struct_field_attrs(cx, &structure_ty, generics, fields, is_enum) - ); + let fields_with_attrs = try!(attr::fields_with_attrs(cx, fields)); - let arms: Vec = fields.iter().zip(field_attrs.iter()) - .filter(|&(_, ref field_attr)| !field_attr.skip_serializing_field()) + let arms: Vec = fields_with_attrs.iter() + .filter(|&&(_, ref attrs)| !attrs.skip_serializing_field()) .enumerate() - .map(|(i, (ref field, ref field_attr))| { - let name = field.ident.expect("struct has unnamed field"); + .map(|(i, &(field, ref attrs))| { + let ident = field.ident.expect("struct has unnamed field"); + let mut field_expr = quote_expr!(cx, self.value.$ident); + if !is_enum { + field_expr = quote_expr!(cx, &$field_expr); + } - let key_expr = field_attr.name().serialize_name_expr(); + let key_expr = attrs.name().serialize_name_expr(); - let stmt = if let Some(expr) = field_attr.skip_serializing_field_if() { - Some(quote_stmt!(cx, if $expr { continue; })) - } else { - None - }; + let continue_if_skip = attrs.skip_serializing_if() + .map(|path| quote_stmt!(cx, if $path($field_expr) { continue })); - let field_expr = match field_attr.serialize_with() { - Some(expr) => expr.clone(), - None => quote_expr!(cx, &self.value.$name), - }; + if let Some(path) = attrs.serialize_with() { + field_expr = wrap_serialize_with(cx, builder, + &structure_ty, generics, &field.ty, path, field_expr) + } let expr = quote_expr!(cx, _serializer.$serializer_method($key_expr, $field_expr) @@ -716,7 +769,7 @@ fn serialize_struct_visitor( quote_arm!(cx, $i => { self.state += 1; - $stmt + $continue_if_skip return Ok(Some(try!($expr))); } ) @@ -734,16 +787,18 @@ fn serialize_struct_visitor( .strip_bounds() .build(); - let len = field_attrs.iter() - .filter(|field_attr| !field_attr.skip_serializing_field()) - .map(|field_attr| { - match field_attr.skip_serializing_field_if() { - Some(expr) => { - quote_expr!(cx, if $expr { 0 } else { 1 }) - } - None => { - quote_expr!(cx, 1) - } + let len = fields_with_attrs.iter() + .filter(|&&(_, ref attrs)| !attrs.skip_serializing_field()) + .map(|&(field, ref attrs)| { + let ident = field.ident.expect("struct has unnamed fields"); + let mut field_expr = quote_expr!(cx, self.value.$ident); + if !is_enum { + field_expr = quote_expr!(cx, &$field_expr); + } + + match attrs.skip_serializing_if() { + Some(path) => quote_expr!(cx, if $path($field_expr) { 0 } else { 1 }), + None => quote_expr!(cx, 1), } }) .fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); @@ -782,3 +837,46 @@ fn serialize_struct_visitor( ).unwrap(), )) } + +fn wrap_serialize_with( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + container_ty: &P, + generics: &ast::Generics, + field_ty: &P, + path: &ast::Path, + value: P, +) -> P { + let where_clause = &generics.where_clause; + + let wrapper_generics = builder.from_generics(generics.clone()) + .add_lifetime_bound("'__a") + .lifetime_name("'__a") + .build(); + + let wrapper_ty = builder.path() + .segment("__SerializeWith") + .with_generics(wrapper_generics.clone()) + .build() + .build(); + + quote_expr!(cx, { + struct __SerializeWith $wrapper_generics $where_clause { + value: &'__a $field_ty, + phantom: ::std::marker::PhantomData<$container_ty>, + } + + impl $wrapper_generics _serde::ser::Serialize for $wrapper_ty $where_clause { + fn serialize<__S>(&self, __s: &mut __S) -> Result<(), __S::Error> + where __S: _serde::ser::Serializer + { + $path(self.value, __s) + } + } + + __SerializeWith { + value: $value, + phantom: ::std::marker::PhantomData::<$container_ty>, + } + }) +} diff --git a/serde_tests/tests/test_gen.rs b/serde_tests/tests/test_gen.rs index 8410b2c3..6f897e08 100644 --- a/serde_tests/tests/test_gen.rs +++ b/serde_tests/tests/test_gen.rs @@ -1,5 +1,6 @@ // These just test that serde_codegen is able to produce code that compiles -// successfully when there are a variety of generics involved. +// successfully when there are a variety of generics and non-(de)serializable +// types involved. extern crate serde; use self::serde::ser::{Serialize, Serializer}; @@ -10,16 +11,16 @@ use self::serde::de::{Deserialize, Deserializer}; #[derive(Serialize, Deserialize)] struct With { t: T, - #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] - i: i32, + #[serde(serialize_with="ser_x", deserialize_with="de_x")] + x: X, } #[derive(Serialize, Deserialize)] struct WithRef<'a, T: 'a> { #[serde(skip_deserializing)] t: Option<&'a T>, - #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] - i: i32, + #[serde(serialize_with="ser_x", deserialize_with="de_x")] + x: X, } #[derive(Serialize, Deserialize)] @@ -40,13 +41,18 @@ struct NoBounds { #[derive(Serialize, Deserialize)] enum EnumWith { - A( - #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] - i32), - B { + Unit, + Newtype( + #[serde(serialize_with="ser_x", deserialize_with="de_x")] + X), + Tuple( + T, + #[serde(serialize_with="ser_x", deserialize_with="de_x")] + X), + Struct { t: T, - #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] - i: i32 }, + #[serde(serialize_with="ser_x", deserialize_with="de_x")] + x: X }, } #[derive(Serialize)] @@ -55,8 +61,23 @@ struct MultipleRef<'a, 'b, 'c, T> where T: 'c, 'c: 'b, 'b: 'a { rrrt: &'a &'b &'c T, } +#[derive(Serialize, Deserialize)] +struct Newtype( + #[serde(serialize_with="ser_x", deserialize_with="de_x")] + X +); + +#[derive(Serialize, Deserialize)] +struct Tuple( + T, + #[serde(serialize_with="ser_x", deserialize_with="de_x")] + X, +); + ////////////////////////////////////////////////////////////////////////// -fn ser_i32(_: &i32, _: &mut S) -> Result<(), S::Error> { panic!() } +// Implements neither Serialize nor Deserialize +struct X; +fn ser_x(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() } +fn de_x(_: &mut D) -> Result { panic!() } -fn de_i32(_: &mut D) -> Result { panic!() }