Auto merge of #367 - dtolnay:default, r=oli-obk

Simplify implementation of #[serde(default=...)]
This commit is contained in:
Homu
2016-06-10 17:57:39 +09:00
4 changed files with 35 additions and 43 deletions
+20 -22
View File
@@ -212,13 +212,24 @@ pub struct FieldAttrs {
skip_serializing_field: bool, skip_serializing_field: bool,
skip_deserializing_field: bool, skip_deserializing_field: bool,
skip_serializing_if: Option<ast::Path>, skip_serializing_if: Option<ast::Path>,
default_expr_if_missing: Option<P<ast::Expr>>, default: FieldDefault,
serialize_with: Option<ast::Path>, serialize_with: Option<ast::Path>,
deserialize_with: Option<ast::Path>, deserialize_with: Option<ast::Path>,
ser_bound: Option<Vec<ast::WherePredicate>>, ser_bound: Option<Vec<ast::WherePredicate>>,
de_bound: Option<Vec<ast::WherePredicate>>, de_bound: Option<Vec<ast::WherePredicate>>,
} }
/// Represents the default to use for a field when deserializing.
#[derive(Debug, PartialEq)]
pub enum FieldDefault {
/// Field must always be specified because it does not have a default.
None,
/// The default is given by `std::default::Default::default()`.
Default,
/// The default is given by this function.
Path(ast::Path),
}
impl FieldAttrs { impl FieldAttrs {
/// Extract out the `#[serde(...)]` attributes from a struct field. /// Extract out the `#[serde(...)]` attributes from a struct field.
pub fn from_field(cx: &ExtCtxt, pub fn from_field(cx: &ExtCtxt,
@@ -236,7 +247,7 @@ impl FieldAttrs {
skip_serializing_field: false, skip_serializing_field: false,
skip_deserializing_field: false, skip_deserializing_field: false,
skip_serializing_if: None, skip_serializing_if: None,
default_expr_if_missing: None, default: FieldDefault::None,
serialize_with: None, serialize_with: None,
deserialize_with: None, deserialize_with: None,
ser_bound: None, ser_bound: None,
@@ -266,17 +277,13 @@ impl FieldAttrs {
// Parse `#[serde(default)]` // Parse `#[serde(default)]`
ast::MetaItemKind::Word(ref name) if name == &"default" => { ast::MetaItemKind::Word(ref name) if name == &"default" => {
let default_expr = builder.expr().default(); field_attrs.default = FieldDefault::Default;
field_attrs.default_expr_if_missing = Some(default_expr);
} }
// Parse `#[serde(default="...")]` // Parse `#[serde(default="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => {
let wrapped_expr = wrap_default( let path = try!(parse_lit_into_path(cx, name, lit));
try!(parse_lit_into_path(cx, name, lit)), field_attrs.default = FieldDefault::Path(path);
);
field_attrs.default_expr_if_missing = Some(wrapped_expr);
} }
// Parse `#[serde(skip_serializing)]` // Parse `#[serde(skip_serializing)]`
@@ -290,9 +297,8 @@ impl FieldAttrs {
// Initialize field to Default::default() unless a different // Initialize field to Default::default() unless a different
// default is specified by `#[serde(default="...")]` // default is specified by `#[serde(default="...")]`
if field_attrs.default_expr_if_missing.is_none() { if field_attrs.default == FieldDefault::None {
let default_expr = builder.expr().default(); field_attrs.default = FieldDefault::Default;
field_attrs.default_expr_if_missing = Some(default_expr);
} }
} }
@@ -363,8 +369,8 @@ impl FieldAttrs {
self.skip_serializing_if.as_ref() self.skip_serializing_if.as_ref()
} }
pub fn default_expr_if_missing(&self) -> Option<&P<ast::Expr>> { pub fn default(&self) -> &FieldDefault {
self.default_expr_if_missing.as_ref() &self.default
} }
pub fn serialize_with(&self) -> Option<&ast::Path> { pub fn serialize_with(&self) -> Option<&ast::Path> {
@@ -584,11 +590,3 @@ fn parse_lit_into_where(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<Vec<
Ok(where_clause.predicates) Ok(where_clause.predicates)
}) })
} }
/// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it
/// from accessing the internal `Deserialize` state.
fn wrap_default(path: ast::Path) -> P<ast::Expr> {
AstBuilder::new().expr().call()
.build_path(path)
.build()
}
+2 -2
View File
@@ -60,13 +60,13 @@ pub fn with_bound<F>(
filter: F, filter: F,
bound: &ast::Path, bound: &ast::Path,
) -> Result<ast::Generics, Error> ) -> Result<ast::Generics, Error>
where F: Fn(&ast::StructField, &attr::FieldAttrs) -> bool, where F: Fn(&attr::FieldAttrs) -> bool,
{ {
Ok(builder.from_generics(generics.clone()) Ok(builder.from_generics(generics.clone())
.with_predicates( .with_predicates(
try!(all_fields_with_attrs(cx, item)) try!(all_fields_with_attrs(cx, item))
.iter() .iter()
.filter(|&&(ref field, ref attrs)| filter(field, attrs)) .filter(|&&(_, ref attrs)| filter(attrs))
.map(|&(ref field, _)| &field.ty) .map(|&(ref field, _)| &field.ty)
// TODO this filter can be removed later, see comment on function // TODO this filter can be removed later, see comment on function
.filter(|ty| contains_generic(ty, generics)) .filter(|ty| contains_generic(ty, generics))
+12 -18
View File
@@ -134,7 +134,7 @@ fn build_impl_generics(
// deserialized by us so we do not generate a bound. Fields with a `bound` // deserialized by us so we do not generate a bound. Fields with a `bound`
// attribute specify their own bound so we do not generate one. All other fields // attribute specify their own bound so we do not generate one. All other fields
// may need a `T: Deserialize` bound where T is the type of the field. // may need a `T: Deserialize` bound where T is the type of the field.
fn needs_deserialize_bound(_: &ast::StructField, attrs: &attr::FieldAttrs) -> bool { fn needs_deserialize_bound(attrs: &attr::FieldAttrs) -> bool {
!attrs.skip_deserializing_field() !attrs.skip_deserializing_field()
&& attrs.deserialize_with().is_none() && attrs.deserialize_with().is_none()
&& attrs.de_bound().is_none() && attrs.de_bound().is_none()
@@ -142,21 +142,8 @@ fn needs_deserialize_bound(_: &ast::StructField, attrs: &attr::FieldAttrs) -> bo
// Fields with a `default` attribute (not `default=...`), and fields with a // Fields with a `default` attribute (not `default=...`), and fields with a
// `skip_deserializing` attribute that do not also have `default=...`. // `skip_deserializing` attribute that do not also have `default=...`.
fn requires_default(field: &ast::StructField, attrs: &attr::FieldAttrs) -> bool { fn requires_default(attrs: &attr::FieldAttrs) -> bool {
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) { attrs.default() == &attr::FieldDefault::Default
for meta_item in meta_items {
match meta_item.node {
ast::MetaItemKind::Word(ref name) if name == &"default" => {
return true
}
ast::MetaItemKind::NameValue(ref name, _) if name == &"default" => {
return false
}
_ => {}
}
}
}
attrs.skip_deserializing_field()
} }
fn deserialize_body( fn deserialize_body(
@@ -1234,9 +1221,16 @@ fn expr_is_missing(
cx: &ExtCtxt, cx: &ExtCtxt,
attrs: &attr::FieldAttrs, attrs: &attr::FieldAttrs,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
if let Some(expr) = attrs.default_expr_if_missing() { match *attrs.default() {
return expr.clone(); attr::FieldDefault::Default => {
return quote_expr!(cx, ::std::default::Default::default());
}
attr::FieldDefault::Path(ref path) => {
return quote_expr!(cx, $path());
}
attr::FieldDefault::None => { /* below */ }
} }
let name = attrs.name().deserialize_name_expr(); let name = attrs.name().deserialize_name_expr();
match attrs.deserialize_with() { match attrs.deserialize_with() {
None => { None => {
+1 -1
View File
@@ -128,7 +128,7 @@ fn build_impl_generics(
// serialized by us so we do not generate a bound. Fields with a `bound` // serialized by us so we do not generate a bound. Fields with a `bound`
// attribute specify their own bound so we do not generate one. All other fields // attribute specify their own bound so we do not generate one. All other fields
// may need a `T: Serialize` bound where T is the type of the field. // may need a `T: Serialize` bound where T is the type of the field.
fn needs_serialize_bound(_: &ast::StructField, attrs: &attr::FieldAttrs) -> bool { fn needs_serialize_bound(attrs: &attr::FieldAttrs) -> bool {
!attrs.skip_serializing_field() !attrs.skip_serializing_field()
&& attrs.serialize_with().is_none() && attrs.serialize_with().is_none()
&& attrs.ser_bound().is_none() && attrs.ser_bound().is_none()