Skip to content

feat(codegen) skip_deserializing #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
11 changes: 11 additions & 0 deletions serde_codegen/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,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>>,
Expand All @@ -204,6 +205,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,
Expand Down Expand Up @@ -249,6 +251,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(
Expand Down Expand Up @@ -325,6 +332,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()
}
Expand Down
182 changes: 100 additions & 82 deletions serde_codegen/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,27 +430,35 @@ 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();

let result = builder.expr().struct_path(struct_path)
.with_id_exprs(
fields.iter()
.enumerate()
.map(|(i, field)| {
.map(|(i, &(field, _))| {
(
match field.ident {
Some(name) => name.clone(),
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
);
Expand All @@ -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 => {
Expand All @@ -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)))
.collect();

let field_attrs: Vec<_> = try!(
fields.iter()
.map(|field| attr::FieldAttrs::from_field(cx, container_ty, generics, field, is_enum))
.collect()
);
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.
let let_values: Vec<ast::Stmt> = field_names.iter()
.map(|field_name| quote_stmt!(cx, let mut $field_name = None;).unwrap())
// 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();


// 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));
}
)
})
.collect::<Vec<_>>();

// 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>());
}
)
})
.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();
// 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())
Expand All @@ -1095,17 +1093,20 @@ 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(),
None => {
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)
}
)
})
)
Expand All @@ -1117,6 +1118,8 @@ fn deserialize_map(
while let Some(key) = try!(visitor.visit_key()) {
match key {
$value_arms
$skipped_arms
$ignored_arm
}
}

Expand All @@ -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()
}
Loading