Support (de)serialize_with in tuples

This commit is contained in:
David Tolnay
2016-05-15 15:54:20 -07:00
parent cc115ca43a
commit 938f42faf6
4 changed files with 353 additions and 311 deletions
+23 -132
View File
@@ -177,31 +177,29 @@ pub struct FieldAttrs {
name: Name,
skip_serializing_field: bool,
skip_deserializing_field: bool,
skip_serializing_field_if: Option<P<ast::Expr>>,
skip_serializing_if: Option<ast::Path>,
default_expr_if_missing: Option<P<ast::Expr>>,
serialize_with: Option<P<ast::Expr>>,
serialize_with: Option<ast::Path>,
deserialize_with: Option<ast::Path>,
}
impl FieldAttrs {
/// Extract out the `#[serde(...)]` attributes from a struct field.
pub fn from_field(cx: &ExtCtxt,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
field: &ast::StructField,
is_enum: bool) -> Result<Self, Error> {
index: usize,
field: &ast::StructField) -> Result<Self, Error> {
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<ast::Expr>> {
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<ast::Expr>> {
self.default_expr_if_missing.as_ref()
}
pub fn serialize_with(&self) -> Option<&P<ast::Expr>> {
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<ast::Ty>,
generics: &ast::Generics,
fields: &[ast::StructField],
is_enum: bool) -> Result<Vec<FieldAttrs>, Error> {
/// Zip together fields and `#[serde(...)]` attributes on those fields.
pub fn fields_with_attrs<'a>(
cx: &ExtCtxt,
fields: &'a [ast::StructField],
) -> Result<Vec<(&'a ast::StructField, FieldAttrs)>, 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<ast::Expr> {
.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<ast::Expr> {
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<ast::Ty>,
generics: &ast::Generics,
field_ident: ast::Ident,
path: ast::Path,
is_enum: bool) -> P<ast::Expr> {
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,
}
})
}