Merge branch 'skip_deserializing' of https://github.com/dtolnay/serde into dtolnay-skip_deserializing

This commit is contained in:
Erick Tryzelaar 2016-04-12 08:52:25 -07:00
commit dbba537b66
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="$path")]` | Call the path to a function `fn() -> T` to build the 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(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 |

View File

@ -176,6 +176,7 @@ impl VariantAttrs {
pub struct FieldAttrs {
name: Name,
skip_serializing_field: bool,
skip_deserializing_field: bool,
skip_serializing_field_if: Option<P<ast::Expr>>,
default_expr_if_missing: Option<P<ast::Expr>>,
serialize_with: Option<P<ast::Expr>>,
@ -199,6 +200,7 @@ impl FieldAttrs {
let mut field_attrs = FieldAttrs {
name: Name::new(field_ident),
skip_serializing_field: false,
skip_deserializing_field: false,
skip_serializing_field_if: None,
default_expr_if_missing: None,
serialize_with: None,
@ -244,6 +246,11 @@ impl FieldAttrs {
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="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => {
let expr = wrap_skip_serializing(
@ -320,6 +327,10 @@ impl FieldAttrs {
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>> {
self.skip_serializing_field_if.as_ref()
}

View File

@ -430,19 +430,27 @@ fn deserialize_struct_as_seq(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
struct_path: ast::Path,
fields: &[ast::StructField],
fields: &[(&ast::StructField, attr::FieldAttrs)],
) -> Result<P<ast::Expr>, Error> {
let let_values: Vec<_> = (0 .. fields.len())
.map(|i| {
let let_values: Vec<_> = fields.iter()
.enumerate()
.map(|(i, &(_, ref attrs))| {
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()
if attrs.skip_deserializing_field() {
let default = builder.expr().default();
quote_stmt!(cx,
let $name = $default;
).unwrap()
} else {
quote_stmt!(cx,
let $name = match try!(visitor.visit()) {
Some(value) => { value },
None => {
return Err(::serde::de::Error::end_of_stream());
}
};
).unwrap()
}
})
.collect();
@ -450,7 +458,7 @@ fn deserialize_struct_as_seq(
.with_id_exprs(
fields.iter()
.enumerate()
.map(|(i, field)| {
.map(|(i, &(field, _))| {
(
match field.ident {
Some(name) => name.clone(),
@ -493,22 +501,21 @@ fn deserialize_struct(
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(
cx,
builder,
type_path.clone(),
fields,
&fields_with_attrs,
));
let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor(
cx,
builder,
type_path.clone(),
&ty,
impl_generics,
fields,
&fields_with_attrs,
container_attrs,
false,
));
let type_name = container_attrs.name().deserialize_name_expr();
@ -750,22 +757,21 @@ fn deserialize_struct_variant(
.id(variant_ident)
.build();
let fields_with_attrs = try!(fields_with_attrs(cx, generics, &ty, fields, true));
let visit_seq_expr = try!(deserialize_struct_as_seq(
cx,
builder,
type_path.clone(),
fields,
&fields_with_attrs,
));
let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor(
cx,
builder,
type_path,
&ty,
generics,
fields,
&fields_with_attrs,
container_attrs,
true,
));
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
@ -966,29 +972,17 @@ fn deserialize_struct_visitor(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
struct_path: ast::Path,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
fields: &[ast::StructField],
fields: &[(&ast::StructField, attr::FieldAttrs)],
container_attrs: &attr::ContainerAttrs,
is_enum: bool,
) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> {
let field_exprs = fields.iter()
.map(|field| {
let field_attrs = try!(
attr::FieldAttrs::from_field(cx,
container_ty,
generics,
field,
is_enum)
);
Ok(field_attrs.name().deserialize_name())
})
.map(|&(_, ref attrs)| attrs.name().deserialize_name())
.collect();
let field_visitor = deserialize_field_visitor(
cx,
builder,
try!(field_exprs),
field_exprs,
container_attrs,
false,
);
@ -997,17 +991,14 @@ fn deserialize_struct_visitor(
cx,
builder,
struct_path,
container_ty,
generics,
fields,
container_attrs,
is_enum,
));
let fields_expr = builder.expr().ref_().slice()
.with_exprs(
fields.iter()
.map(|field| {
.map(|&(field, _)| {
match field.ident {
Some(name) => builder.expr().str(name),
None => {
@ -1029,62 +1020,69 @@ fn deserialize_map(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
struct_path: ast::Path,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
fields: &[ast::StructField],
fields: &[(&ast::StructField, attr::FieldAttrs)],
container_attrs: &attr::ContainerAttrs,
is_enum: bool,
) -> Result<P<ast::Expr>, Error> {
// Create the field names for the fields.
let field_names: Vec<ast::Ident> = (0 .. fields.len())
.map(|i| builder.id(format!("__field{}", i)))
let fields_attrs_names = fields.iter()
.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();
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.
let value_arms = field_attrs.iter().zip(field_names.iter())
.map(|(field_attr, field_name)| {
let expr = match field_attr.deserialize_with() {
let value_arms = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.map(|&(_, ref attrs, name)| {
let expr = match attrs.deserialize_with() {
Some(expr) => expr.clone(),
None => quote_expr!(cx, visitor.visit_value()),
};
quote_arm!(cx,
__Field::$field_name => {
$field_name = Some(try!($expr));
__Field::$name => {
$name = Some(try!($expr));
}
)
})
.chain(ignored_arm.into_iter())
.collect::<Vec<_>>();
let extract_values = field_attrs.iter().zip(field_names.iter())
.map(|(field_attr, field_name)| {
let missing_expr = field_attr.expr_is_missing();
// Match arms to ignore value for fields that have `skip_deserializing`.
// Ignored even if `deny_unknown_fields` is set.
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,
let $field_name = match $field_name {
Some($field_name) => $field_name,
let $name = match $name {
Some($name) => $name,
None => $missing_expr
};
).unwrap())
@ -1095,9 +1093,8 @@ fn deserialize_map(
let result = builder.expr().struct_path(struct_path)
.with_id_exprs(
fields.iter()
.zip(field_names.iter())
.map(|(field, field_name)| {
fields_attrs_names.iter()
.map(|&(field, attrs, name)| {
(
match field.ident {
Some(name) => name.clone(),
@ -1105,7 +1102,11 @@ fn deserialize_map(
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()) {
match key {
$value_arms
$skipped_arms
$ignored_arm
}
}
@ -1127,3 +1130,18 @@ fn deserialize_map(
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 {
a: i32,
b: i32,
#[serde(skip_deserializing)]
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! {
@ -524,7 +536,40 @@ declare_tests! {
],
}
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::MapSep,
Token::Str("a"),
@ -537,9 +582,13 @@ declare_tests! {
Token::MapSep,
Token::Str("c"),
Token::I32(3),
Token::MapSep,
Token::Str("d"),
Token::I32(4),
Token::MapEnd,
],
Struct { a: 1, b: 2, c: 3 } => vec![
Struct { a: 1, b: 2, c: 0 } => vec![
Token::StructStart("Struct", Some(3)),
Token::StructSep,
Token::Str("a"),
@ -552,6 +601,10 @@ declare_tests! {
Token::StructSep,
Token::Str("c"),
Token::I32(3),
Token::StructSep,
Token::Str("d"),
Token::I32(4),
Token::StructEnd,
],
}
@ -638,12 +691,22 @@ fn test_net_ipaddr() {
);
}
#[test]
fn test_enum_error() {
assert_de_tokens_error::<Enum>(
declare_error_tests! {
test_unknown_variant<Enum> {
vec![
Token::EnumUnit("Enum", "Foo"),
],
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),
}
}