diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index b59e860b..5dd14c25 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -212,13 +212,24 @@ pub struct FieldAttrs { skip_serializing_field: bool, skip_deserializing_field: bool, skip_serializing_if: Option, - default_expr_if_missing: Option>, + default: FieldDefault, serialize_with: Option, deserialize_with: Option, ser_bound: Option>, de_bound: Option>, } +/// 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 { /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn from_field(cx: &ExtCtxt, @@ -236,7 +247,7 @@ impl FieldAttrs { skip_serializing_field: false, skip_deserializing_field: false, skip_serializing_if: None, - default_expr_if_missing: None, + default: FieldDefault::None, serialize_with: None, deserialize_with: None, ser_bound: None, @@ -266,17 +277,13 @@ impl FieldAttrs { // Parse `#[serde(default)]` ast::MetaItemKind::Word(ref name) if name == &"default" => { - let default_expr = builder.expr().default(); - field_attrs.default_expr_if_missing = Some(default_expr); + field_attrs.default = FieldDefault::Default; } // Parse `#[serde(default="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => { - let wrapped_expr = wrap_default( - try!(parse_lit_into_path(cx, name, lit)), - ); - - field_attrs.default_expr_if_missing = Some(wrapped_expr); + let path = try!(parse_lit_into_path(cx, name, lit)); + field_attrs.default = FieldDefault::Path(path); } // Parse `#[serde(skip_serializing)]` @@ -290,9 +297,8 @@ impl FieldAttrs { // Initialize field to Default::default() unless a different // default is specified by `#[serde(default="...")]` - if field_attrs.default_expr_if_missing.is_none() { - let default_expr = builder.expr().default(); - field_attrs.default_expr_if_missing = Some(default_expr); + if field_attrs.default == FieldDefault::None { + field_attrs.default = FieldDefault::Default; } } @@ -363,8 +369,8 @@ impl FieldAttrs { self.skip_serializing_if.as_ref() } - pub fn default_expr_if_missing(&self) -> Option<&P> { - self.default_expr_if_missing.as_ref() + pub fn default(&self) -> &FieldDefault { + &self.default } 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 P { - AstBuilder::new().expr().call() - .build_path(path) - .build() -} diff --git a/serde_codegen/src/bound.rs b/serde_codegen/src/bound.rs index cabdb6ed..bd6f1c7e 100644 --- a/serde_codegen/src/bound.rs +++ b/serde_codegen/src/bound.rs @@ -60,13 +60,13 @@ pub fn with_bound( filter: F, bound: &ast::Path, ) -> Result - where F: Fn(&ast::StructField, &attr::FieldAttrs) -> bool, + where F: Fn(&attr::FieldAttrs) -> bool, { Ok(builder.from_generics(generics.clone()) .with_predicates( try!(all_fields_with_attrs(cx, item)) .iter() - .filter(|&&(ref field, ref attrs)| filter(field, attrs)) + .filter(|&&(_, ref attrs)| filter(attrs)) .map(|&(ref field, _)| &field.ty) // TODO this filter can be removed later, see comment on function .filter(|ty| contains_generic(ty, generics)) diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 7a81cb3f..8864ea46 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -134,7 +134,7 @@ fn build_impl_generics( // 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 // 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.deserialize_with().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 // `skip_deserializing` attribute that do not also have `default=...`. -fn requires_default(field: &ast::StructField, attrs: &attr::FieldAttrs) -> bool { - for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) { - 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 requires_default(attrs: &attr::FieldAttrs) -> bool { + attrs.default() == &attr::FieldDefault::Default } fn deserialize_body( @@ -1234,9 +1221,16 @@ fn expr_is_missing( cx: &ExtCtxt, attrs: &attr::FieldAttrs, ) -> P { - if let Some(expr) = attrs.default_expr_if_missing() { - return expr.clone(); + match *attrs.default() { + 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(); match attrs.deserialize_with() { None => { diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 3ab17fb8..dcc29ecb 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -128,7 +128,7 @@ fn build_impl_generics( // 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 // 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.serialize_with().is_none() && attrs.ser_bound().is_none()