feat(codegen): Add #[serde(serialize_with="...")]

This allows a field to be serialized with an expression instead
of the default serializer. This simplifies serializing a struct
or enum that contains an external type that doesn't implement
`serde::Serialize`. This expression is passed a variable
`serializer` that needs to be used to serialize the expression.
This commit is contained in:
Erick Tryzelaar
2016-02-15 17:39:46 -08:00
parent de89f95f31
commit 001cb7ab01
3 changed files with 196 additions and 1 deletions
+70
View File
@@ -170,6 +170,7 @@ pub struct FieldAttrs {
skip_serializing_field_if_empty: bool,
skip_serializing_field_if_none: bool,
default_expr_if_missing: Option<P<ast::Expr>>,
serialize_with: Option<P<ast::Expr>>,
}
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<ast::Expr>> {
self.serialize_with.as_ref()
}
}
@@ -445,3 +463,55 @@ fn wrap_skip_serializing(cx: &ExtCtxt,
self.value.__serde_should_skip_serializing()
})
}
/// 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<ast::Ty>,
generics: &ast::Generics,
expr: P<ast::Expr>) -> P<ast::Expr> {
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,
}
})
}