feat(codegen) skip_deserializing

This commit is contained in:
David Tolnay 2016-03-06 23:27:12 -08:00
parent a84b6aaedd
commit 87393b61bb
4 changed files with 182 additions and 89 deletions

View File

@ -714,6 +714,7 @@ Field Annotations:
| `#[serde(default)]` | If the value is not specified, use the `Default::default()` | | `#[serde(default)]` | If the value is not specified, use the `Default::default()` |
| `#[serde(default="$path")]` | Call the path to a function `fn() -> T` to build the value | | `#[serde(default="$path")]` | Call the path to a function `fn() -> T` to build the value |
| `#[serde(skip_serializing)]` | Do not serialize this value | | `#[serde(skip_serializing)]` | Do not serialize this value |
| `#[serde(skip_deserializing)]` | Always use `Default::default()` instead of deserializing this value |
| `#[serde(skip_serializing_if="$path")]` | Do not serialize this value if this function `fn(&T) -> bool` returns `false` | | `#[serde(skip_serializing_if="$path")]` | Do not serialize this value if this function `fn(&T) -> bool` returns `false` |
| `#[serde(serialize_with="$path")]` | Call a function `fn<T, S>(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value | | `#[serde(serialize_with="$path")]` | Call a function `fn<T, S>(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value |
| `#[serde(deserialize_with="$path")]` | Call a function `fn<T, D>(&mut D) -> Result<T, D::Error> where D: Deserializer` to deserialize this value | | `#[serde(deserialize_with="$path")]` | Call a function `fn<T, D>(&mut D) -> Result<T, D::Error> where D: Deserializer` to deserialize this value |

View File

@ -181,6 +181,7 @@ impl VariantAttrs {
pub struct FieldAttrs { pub struct FieldAttrs {
name: Name, name: Name,
skip_serializing_field: bool, skip_serializing_field: bool,
skip_deserializing_field: bool,
skip_serializing_field_if: Option<P<ast::Expr>>, skip_serializing_field_if: Option<P<ast::Expr>>,
default_expr_if_missing: Option<P<ast::Expr>>, default_expr_if_missing: Option<P<ast::Expr>>,
serialize_with: Option<P<ast::Expr>>, serialize_with: Option<P<ast::Expr>>,
@ -204,6 +205,7 @@ impl FieldAttrs {
let mut field_attrs = FieldAttrs { let mut field_attrs = FieldAttrs {
name: Name::new(field_ident), name: Name::new(field_ident),
skip_serializing_field: false, skip_serializing_field: false,
skip_deserializing_field: false,
skip_serializing_field_if: None, skip_serializing_field_if: None,
default_expr_if_missing: None, default_expr_if_missing: None,
serialize_with: None, serialize_with: None,
@ -249,6 +251,11 @@ impl FieldAttrs {
field_attrs.skip_serializing_field = true; field_attrs.skip_serializing_field = true;
} }
// Parse `#[serde(skip_deserializing)]`
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
field_attrs.skip_deserializing_field = true;
}
// Parse `#[serde(skip_serializing_if="...")]` // Parse `#[serde(skip_serializing_if="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => {
let expr = wrap_skip_serializing( let expr = wrap_skip_serializing(
@ -325,6 +332,10 @@ impl FieldAttrs {
self.skip_serializing_field self.skip_serializing_field
} }
pub fn skip_deserializing_field(&self) -> bool {
self.skip_deserializing_field
}
pub fn skip_serializing_field_if(&self) -> Option<&P<ast::Expr>> { pub fn skip_serializing_field_if(&self) -> Option<&P<ast::Expr>> {
self.skip_serializing_field_if.as_ref() self.skip_serializing_field_if.as_ref()
} }

View File

@ -430,19 +430,27 @@ fn deserialize_struct_as_seq(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
struct_path: ast::Path, struct_path: ast::Path,
fields: &[ast::StructField], fields: &[(&ast::StructField, attr::FieldAttrs)],
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let let_values: Vec<_> = (0 .. fields.len()) let let_values: Vec<_> = fields.iter()
.map(|i| { .enumerate()
.map(|(i, &(_, ref attrs))| {
let name = builder.id(format!("__field{}", i)); let name = builder.id(format!("__field{}", i));
quote_stmt!(cx, if attrs.skip_deserializing_field() {
let $name = match try!(visitor.visit()) { let default = builder.expr().default();
Some(value) => { value }, quote_stmt!(cx,
None => { let $name = $default;
return Err(::serde::de::Error::end_of_stream()); ).unwrap()
} } else {
}; quote_stmt!(cx,
).unwrap() let $name = match try!(visitor.visit()) {
Some(value) => { value },
None => {
return Err(::serde::de::Error::end_of_stream());
}
};
).unwrap()
}
}) })
.collect(); .collect();
@ -450,7 +458,7 @@ fn deserialize_struct_as_seq(
.with_id_exprs( .with_id_exprs(
fields.iter() fields.iter()
.enumerate() .enumerate()
.map(|(i, field)| { .map(|(i, &(field, _))| {
( (
match field.ident { match field.ident {
Some(name) => name.clone(), Some(name) => name.clone(),
@ -493,22 +501,21 @@ fn deserialize_struct(
let type_path = builder.path().id(type_ident).build(); let type_path = builder.path().id(type_ident).build();
let fields_with_attrs = try!(fields_with_attrs(cx, impl_generics, &ty, fields, false));
let visit_seq_expr = try!(deserialize_struct_as_seq( let visit_seq_expr = try!(deserialize_struct_as_seq(
cx, cx,
builder, builder,
type_path.clone(), type_path.clone(),
fields, &fields_with_attrs,
)); ));
let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor( let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor(
cx, cx,
builder, builder,
type_path.clone(), type_path.clone(),
&ty, &fields_with_attrs,
impl_generics,
fields,
container_attrs, container_attrs,
false,
)); ));
let type_name = container_attrs.name().deserialize_name_expr(); let type_name = container_attrs.name().deserialize_name_expr();
@ -750,22 +757,21 @@ fn deserialize_struct_variant(
.id(variant_ident) .id(variant_ident)
.build(); .build();
let fields_with_attrs = try!(fields_with_attrs(cx, generics, &ty, fields, true));
let visit_seq_expr = try!(deserialize_struct_as_seq( let visit_seq_expr = try!(deserialize_struct_as_seq(
cx, cx,
builder, builder,
type_path.clone(), type_path.clone(),
fields, &fields_with_attrs,
)); ));
let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor( let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor(
cx, cx,
builder, builder,
type_path, type_path,
&ty, &fields_with_attrs,
generics,
fields,
container_attrs, container_attrs,
true,
)); ));
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
@ -966,29 +972,17 @@ fn deserialize_struct_visitor(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
struct_path: ast::Path, struct_path: ast::Path,
container_ty: &P<ast::Ty>, fields: &[(&ast::StructField, attr::FieldAttrs)],
generics: &ast::Generics,
fields: &[ast::StructField],
container_attrs: &attr::ContainerAttrs, container_attrs: &attr::ContainerAttrs,
is_enum: bool,
) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> { ) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> {
let field_exprs = fields.iter() let field_exprs = fields.iter()
.map(|field| { .map(|&(_, ref attrs)| attrs.name().deserialize_name())
let field_attrs = try!(
attr::FieldAttrs::from_field(cx,
container_ty,
generics,
field,
is_enum)
);
Ok(field_attrs.name().deserialize_name())
})
.collect(); .collect();
let field_visitor = deserialize_field_visitor( let field_visitor = deserialize_field_visitor(
cx, cx,
builder, builder,
try!(field_exprs), field_exprs,
container_attrs, container_attrs,
false, false,
); );
@ -997,17 +991,14 @@ fn deserialize_struct_visitor(
cx, cx,
builder, builder,
struct_path, struct_path,
container_ty,
generics,
fields, fields,
container_attrs, container_attrs,
is_enum,
)); ));
let fields_expr = builder.expr().ref_().slice() let fields_expr = builder.expr().ref_().slice()
.with_exprs( .with_exprs(
fields.iter() fields.iter()
.map(|field| { .map(|&(field, _)| {
match field.ident { match field.ident {
Some(name) => builder.expr().str(name), Some(name) => builder.expr().str(name),
None => { None => {
@ -1029,62 +1020,69 @@ fn deserialize_map(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
struct_path: ast::Path, struct_path: ast::Path,
container_ty: &P<ast::Ty>, fields: &[(&ast::StructField, attr::FieldAttrs)],
generics: &ast::Generics,
fields: &[ast::StructField],
container_attrs: &attr::ContainerAttrs, container_attrs: &attr::ContainerAttrs,
is_enum: bool,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
// Create the field names for the fields. // Create the field names for the fields.
let field_names: Vec<ast::Ident> = (0 .. fields.len()) let fields_attrs_names = fields.iter()
.map(|i| builder.id(format!("__field{}", i))) .enumerate()
.map(|(i, &(ref field, ref attrs))|
(field, attrs, builder.id(format!("__field{}", i))))
.collect::<Vec<_>>();
// Declare each field that will be deserialized.
let let_values: Vec<ast::Stmt> = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.map(|&(_, _, name)| quote_stmt!(cx, let mut $name = None;).unwrap())
.collect(); .collect();
let field_attrs: Vec<_> = try!(
fields.iter()
.map(|field| attr::FieldAttrs::from_field(cx, container_ty, generics, field, is_enum))
.collect()
);
// Declare each field.
let let_values: Vec<ast::Stmt> = field_names.iter()
.map(|field_name| quote_stmt!(cx, let mut $field_name = None;).unwrap())
.collect();
// Visit ignored values to consume them
let ignored_arm = if container_attrs.deny_unknown_fields() {
None
} else {
Some(quote_arm!(cx,
_ => { try!(visitor.visit_value::<::serde::de::impls::IgnoredAny>()); }
))
};
// Match arms to extract a value for a field. // Match arms to extract a value for a field.
let value_arms = field_attrs.iter().zip(field_names.iter()) let value_arms = fields_attrs_names.iter()
.map(|(field_attr, field_name)| { .filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
let expr = match field_attr.deserialize_with() { .map(|&(_, ref attrs, name)| {
let expr = match attrs.deserialize_with() {
Some(expr) => expr.clone(), Some(expr) => expr.clone(),
None => quote_expr!(cx, visitor.visit_value()), None => quote_expr!(cx, visitor.visit_value()),
}; };
quote_arm!(cx, quote_arm!(cx,
__Field::$field_name => { __Field::$name => {
$field_name = Some(try!($expr)); $name = Some(try!($expr));
} }
) )
}) })
.chain(ignored_arm.into_iter())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let extract_values = field_attrs.iter().zip(field_names.iter()) // Match arms to ignore value for fields that have `skip_deserializing`.
.map(|(field_attr, field_name)| { // Ignored even if `deny_unknown_fields` is set.
let missing_expr = field_attr.expr_is_missing(); let skipped_arms = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| attrs.skip_deserializing_field())
.map(|&(_, _, name)| {
quote_arm!(cx,
__Field::$name => {
try!(visitor.visit_value::<::serde::de::impls::IgnoredAny>());
}
)
})
.collect::<Vec<_>>();
// Visit ignored values to consume them
let ignored_arm = if !container_attrs.deny_unknown_fields() {
Some(quote_arm!(cx,
_ => { try!(visitor.visit_value::<::serde::de::impls::IgnoredAny>()); }
))
} else {
None
};
let extract_values = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.map(|&(_, ref attrs, name)| {
let missing_expr = attrs.expr_is_missing();
Ok(quote_stmt!(cx, Ok(quote_stmt!(cx,
let $field_name = match $field_name { let $name = match $name {
Some($field_name) => $field_name, Some($name) => $name,
None => $missing_expr None => $missing_expr
}; };
).unwrap()) ).unwrap())
@ -1095,9 +1093,8 @@ fn deserialize_map(
let result = builder.expr().struct_path(struct_path) let result = builder.expr().struct_path(struct_path)
.with_id_exprs( .with_id_exprs(
fields.iter() fields_attrs_names.iter()
.zip(field_names.iter()) .map(|&(field, attrs, name)| {
.map(|(field, field_name)| {
( (
match field.ident { match field.ident {
Some(name) => name.clone(), Some(name) => name.clone(),
@ -1105,7 +1102,11 @@ fn deserialize_map(
cx.span_bug(field.span, "struct contains unnamed fields") cx.span_bug(field.span, "struct contains unnamed fields")
} }
}, },
builder.expr().id(field_name), if attrs.skip_deserializing_field() {
builder.expr().default()
} else {
builder.expr().id(name)
}
) )
}) })
) )
@ -1117,6 +1118,8 @@ fn deserialize_map(
while let Some(key) = try!(visitor.visit_key()) { while let Some(key) = try!(visitor.visit_key()) {
match key { match key {
$value_arms $value_arms
$skipped_arms
$ignored_arm
} }
} }
@ -1127,3 +1130,18 @@ fn deserialize_map(
Ok($result) Ok($result)
})) }))
} }
fn fields_with_attrs<'a>(
cx: &ExtCtxt,
generics: &ast::Generics,
ty: &P<ast::Ty>,
fields: &'a [ast::StructField],
is_enum: bool
) -> Result<Vec<(&'a ast::StructField, attr::FieldAttrs)>, Error> {
fields.iter()
.map(|field| {
let attrs = try!(attr::FieldAttrs::from_field(cx, &ty, generics, field, is_enum));
Ok((field, attrs))
})
.collect()
}

View File

@ -24,6 +24,7 @@ struct TupleStruct(i32, i32, i32);
struct Struct { struct Struct {
a: i32, a: i32,
b: i32, b: i32,
#[serde(skip_deserializing)]
c: i32, c: i32,
} }
@ -60,6 +61,17 @@ macro_rules! declare_tests {
} }
} }
macro_rules! declare_error_tests {
($($name:ident<$target:ident> { $tokens:expr, $expected:expr, })+) => {
$(
#[test]
fn $name() {
assert_de_tokens_error::<$target>($tokens, $expected);
}
)+
}
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
declare_tests! { declare_tests! {
@ -524,7 +536,40 @@ declare_tests! {
], ],
} }
test_struct { test_struct {
Struct { a: 1, b: 2, c: 3 } => vec![ Struct { a: 1, b: 2, c: 0 } => vec![
Token::MapStart(Some(3)),
Token::MapSep,
Token::Str("a"),
Token::I32(1),
Token::MapSep,
Token::Str("b"),
Token::I32(2),
Token::MapEnd,
],
Struct { a: 1, b: 2, c: 0 } => vec![
Token::StructStart("Struct", Some(3)),
Token::StructSep,
Token::Str("a"),
Token::I32(1),
Token::StructSep,
Token::Str("b"),
Token::I32(2),
Token::StructEnd,
],
Struct { a: 1, b: 2, c: 0 } => vec![
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
Token::SeqSep,
Token::I32(2),
Token::SeqEnd,
],
}
test_struct_with_skip {
Struct { a: 1, b: 2, c: 0 } => vec![
Token::MapStart(Some(3)), Token::MapStart(Some(3)),
Token::MapSep, Token::MapSep,
Token::Str("a"), Token::Str("a"),
@ -537,9 +582,13 @@ declare_tests! {
Token::MapSep, Token::MapSep,
Token::Str("c"), Token::Str("c"),
Token::I32(3), Token::I32(3),
Token::MapSep,
Token::Str("d"),
Token::I32(4),
Token::MapEnd, Token::MapEnd,
], ],
Struct { a: 1, b: 2, c: 3 } => vec![ Struct { a: 1, b: 2, c: 0 } => vec![
Token::StructStart("Struct", Some(3)), Token::StructStart("Struct", Some(3)),
Token::StructSep, Token::StructSep,
Token::Str("a"), Token::Str("a"),
@ -552,6 +601,10 @@ declare_tests! {
Token::StructSep, Token::StructSep,
Token::Str("c"), Token::Str("c"),
Token::I32(3), Token::I32(3),
Token::StructSep,
Token::Str("d"),
Token::I32(4),
Token::StructEnd, Token::StructEnd,
], ],
} }
@ -638,12 +691,22 @@ fn test_net_ipaddr() {
); );
} }
#[test] declare_error_tests! {
fn test_enum_error() { test_unknown_variant<Enum> {
assert_de_tokens_error::<Enum>(
vec![ vec![
Token::EnumUnit("Enum", "Foo"), Token::EnumUnit("Enum", "Foo"),
], ],
Error::UnknownVariantError("Foo".to_owned()), Error::UnknownVariantError("Foo".to_owned()),
) }
test_struct_seq_too_long<Struct> {
vec![
Token::SeqStart(Some(4)),
Token::SeqSep, Token::I32(1),
Token::SeqSep, Token::I32(2),
Token::SeqSep, Token::I32(3),
Token::SeqSep, Token::I32(4),
Token::SeqEnd,
],
Error::UnexpectedToken(Token::SeqSep),
}
} }