diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index b440af7e..1ef63530 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -170,6 +170,7 @@ pub struct FieldAttrs { skip_serializing_field_if_empty: bool, skip_serializing_field_if_none: bool, default_expr_if_missing: Option
>, + serialize_with: Option
>,
}
impl FieldAttrs {
@@ -194,6 +195,7 @@ impl FieldAttrs {
skip_serializing_field_if_empty: false,
skip_serializing_field_if_none: false,
default_expr_if_missing: None,
+ serialize_with: None,
};
for meta_items in field.node.attrs.iter().filter_map(get_serde_meta_items) {
@@ -257,6 +259,18 @@ impl FieldAttrs {
field_attrs.skip_serializing_field_if_empty = true;
}
+ // Parse `#[serde(serialize_with="...")]`
+ ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => {
+ let expr = wrap_serialize_with(
+ cx,
+ container_ty,
+ generics,
+ try!(parse_lit_into_expr(cx, name, lit)),
+ );
+
+ field_attrs.serialize_with = Some(expr);
+ }
+
_ => {
cx.span_err(
meta_item.span,
@@ -324,6 +338,10 @@ impl FieldAttrs {
pub fn skip_serializing_field_if_none(&self) -> bool {
self.skip_serializing_field_if_none
}
+
+ pub fn serialize_with(&self) -> Option<&P(&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(&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(&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(&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/ser.rs b/serde_codegen/src/ser.rs
index a604d298..fdf42883 100644
--- a/serde_codegen/src/ser.rs
+++ b/serde_codegen/src/ser.rs
@@ -672,8 +672,13 @@ fn serialize_struct_visitor(
}
};
+ let field_expr = match field_attr.serialize_with() {
+ Some(expr) => expr.clone(),
+ None => quote_expr!(cx, &self.value.$name),
+ };
+
let expr = quote_expr!(cx,
- serializer.$serializer_method($key_expr, &self.value.$name)
+ serializer.$serializer_method($key_expr, $field_expr)
);
quote_arm!(cx,
diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs
index 831c32cd..bd640a62 100644
--- a/serde_tests/tests/test_annotations.rs
+++ b/serde_tests/tests/test_annotations.rs
@@ -1,3 +1,6 @@
+use std::default::Default;
+use serde::{Serialize, Serializer};
+
use token::{
Error,
Token,
@@ -11,12 +14,25 @@ trait Trait {
fn my_default() -> Self;
fn should_skip(&self) -> bool;
+
+ fn serialize_with(&self, ser: &mut S) -> Result<(), S::Error>
+ where S: Serializer;
}
impl Trait for i32 {
fn my_default() -> Self { 123 }
fn should_skip(&self) -> bool { *self == 123 }
+
+ fn serialize_with(&self, ser: &mut S) -> Result<(), S::Error>
+ where S: Serializer
+ {
+ if *self == 123 {
+ true.serialize(ser)
+ } else {
+ false.serialize(ser)
+ }
+ }
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
@@ -502,3 +518,107 @@ fn test_skip_serializing_enum() {
]
);
}
+
+#[derive(Debug, PartialEq, Serialize)]
+struct SerializeWithStruct<'a, B> where B: Trait {
+ a: &'a i8,
+ #[serde(serialize_with="self.b.serialize_with(serializer)")]
+ b: B,
+}
+
+#[test]
+fn test_serialize_with_struct() {
+ let a = 1;
+ assert_ser_tokens(
+ &SerializeWithStruct {
+ a: &a,
+ b: 2,
+ },
+ &[
+ Token::StructStart("SerializeWithStruct", Some(2)),
+
+ Token::StructSep,
+ Token::Str("a"),
+ Token::I8(1),
+
+ Token::StructSep,
+ Token::Str("b"),
+ Token::Bool(false),
+
+ Token::StructEnd,
+ ]
+ );
+
+ assert_ser_tokens(
+ &SerializeWithStruct {
+ a: &a,
+ b: 123,
+ },
+ &[
+ Token::StructStart("SerializeWithStruct", Some(2)),
+
+ Token::StructSep,
+ Token::Str("a"),
+ Token::I8(1),
+
+ Token::StructSep,
+ Token::Str("b"),
+ Token::Bool(true),
+
+ Token::StructEnd,
+ ]
+ );
+}
+
+#[derive(Debug, PartialEq, Serialize)]
+enum SerializeWithEnum<'a, B> where B: Trait {
+ Struct {
+ a: &'a i8,
+ #[serde(serialize_with="self.b.serialize_with(serializer)")]
+ b: B,
+ }
+}
+
+#[test]
+fn test_serialize_with_enum() {
+ let a = 1;
+ assert_ser_tokens(
+ &SerializeWithEnum::Struct {
+ a: &a,
+ b: 2,
+ },
+ &[
+ Token::EnumMapStart("SerializeWithEnum", "Struct", Some(2)),
+
+ Token::EnumMapSep,
+ Token::Str("a"),
+ Token::I8(1),
+
+ Token::EnumMapSep,
+ Token::Str("b"),
+ Token::Bool(false),
+
+ Token::EnumMapEnd,
+ ]
+ );
+
+ assert_ser_tokens(
+ &SerializeWithEnum::Struct {
+ a: &a,
+ b: 123,
+ },
+ &[
+ Token::EnumMapStart("SerializeWithEnum", "Struct", Some(2)),
+
+ Token::EnumMapSep,
+ Token::Str("a"),
+ Token::I8(1),
+
+ Token::EnumMapSep,
+ Token::Str("b"),
+ Token::Bool(true),
+
+ Token::EnumMapEnd,
+ ]
+ );
+}