mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-23 00:08:01 +00:00
Implemented disallow_unknown
* Added codegen for disallow_unknown * ... with new default to ignore unknown values during deserialization * Added ContainerAttrs
This commit is contained in:
@@ -243,3 +243,66 @@ impl<'a> FieldAttrsBuilder<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents container (e.g. struct) attribute information
|
||||
#[derive(Debug)]
|
||||
pub struct ContainerAttrs {
|
||||
disallow_unknown: bool,
|
||||
}
|
||||
|
||||
impl ContainerAttrs {
|
||||
pub fn disallow_unknown(&self) -> bool {
|
||||
self.disallow_unknown
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContainerAttrsBuilder {
|
||||
disallow_unknown: bool,
|
||||
}
|
||||
|
||||
impl ContainerAttrsBuilder {
|
||||
pub fn new() -> ContainerAttrsBuilder {
|
||||
ContainerAttrsBuilder {
|
||||
disallow_unknown: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attrs(self, attrs: &[ast::Attribute]) -> ContainerAttrsBuilder {
|
||||
attrs.iter().fold(self, ContainerAttrsBuilder::attr)
|
||||
}
|
||||
|
||||
pub fn attr(self, attr: &ast::Attribute) -> ContainerAttrsBuilder {
|
||||
match attr.node.value.node {
|
||||
ast::MetaList(ref name, ref items) if name == &"serde" => {
|
||||
attr::mark_used(&attr);
|
||||
items.iter().fold(self, ContainerAttrsBuilder::meta_item)
|
||||
}
|
||||
_ => {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta_item(self, meta_item: &P<ast::MetaItem>) -> ContainerAttrsBuilder {
|
||||
match meta_item.node {
|
||||
ast::MetaWord(ref name) if name == &"disallow_unknown" => {
|
||||
self.disallow_unknown()
|
||||
}
|
||||
_ => {
|
||||
// Ignore unknown meta variables for now.
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disallow_unknown(mut self) -> ContainerAttrsBuilder {
|
||||
self.disallow_unknown = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> ContainerAttrs {
|
||||
ContainerAttrs {
|
||||
disallow_unknown: self.disallow_unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+57
-5
@@ -14,7 +14,7 @@ use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::ptr::P;
|
||||
|
||||
use attr;
|
||||
use attr::{self, ContainerAttrs};
|
||||
use field;
|
||||
|
||||
pub fn expand_derive_deserialize(
|
||||
@@ -82,6 +82,8 @@ fn deserialize_body(
|
||||
impl_generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
) -> P<ast::Expr> {
|
||||
let container_attrs = field::container_attrs(cx, item);
|
||||
|
||||
match item.node {
|
||||
ast::ItemStruct(ref variant_data, _) => {
|
||||
deserialize_item_struct(
|
||||
@@ -91,6 +93,7 @@ fn deserialize_body(
|
||||
impl_generics,
|
||||
ty,
|
||||
variant_data,
|
||||
&container_attrs,
|
||||
)
|
||||
}
|
||||
ast::ItemEnum(ref enum_def, _) => {
|
||||
@@ -101,6 +104,7 @@ fn deserialize_body(
|
||||
impl_generics,
|
||||
ty,
|
||||
enum_def,
|
||||
&container_attrs,
|
||||
)
|
||||
}
|
||||
_ => cx.bug("expected ItemStruct or ItemEnum in #[derive(Deserialize)]")
|
||||
@@ -114,6 +118,7 @@ fn deserialize_item_struct(
|
||||
impl_generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
variant_data: &ast::VariantData,
|
||||
container_attrs: &ContainerAttrs,
|
||||
) -> P<ast::Expr> {
|
||||
match *variant_data {
|
||||
ast::VariantData::Unit(_) => {
|
||||
@@ -158,6 +163,7 @@ fn deserialize_item_struct(
|
||||
impl_generics,
|
||||
ty,
|
||||
fields,
|
||||
container_attrs,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -461,6 +467,7 @@ fn deserialize_struct(
|
||||
impl_generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
fields: &[ast::StructField],
|
||||
container_attrs: &ContainerAttrs,
|
||||
) -> P<ast::Expr> {
|
||||
let where_clause = &impl_generics.where_clause;
|
||||
|
||||
@@ -485,6 +492,7 @@ fn deserialize_struct(
|
||||
builder,
|
||||
type_path.clone(),
|
||||
fields,
|
||||
container_attrs
|
||||
);
|
||||
|
||||
let type_name = builder.expr().str(type_ident);
|
||||
@@ -525,6 +533,7 @@ fn deserialize_item_enum(
|
||||
impl_generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
enum_def: &EnumDef,
|
||||
container_attrs: &ContainerAttrs
|
||||
) -> P<ast::Expr> {
|
||||
let where_clause = &impl_generics.where_clause;
|
||||
|
||||
@@ -541,7 +550,8 @@ fn deserialize_item_enum(
|
||||
.default()
|
||||
.build()
|
||||
})
|
||||
.collect()
|
||||
.collect(),
|
||||
container_attrs,
|
||||
);
|
||||
|
||||
let variants_expr = builder.expr().addr_of().slice()
|
||||
@@ -557,6 +567,12 @@ fn deserialize_item_enum(
|
||||
const VARIANTS: &'static [&'static str] = $variants_expr;
|
||||
).unwrap();
|
||||
|
||||
let ignored_arm = if !container_attrs.disallow_unknown() {
|
||||
Some(quote_arm!(cx, __Field::__ignore => { Err(::serde::de::Error::end_of_stream()) }))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Match arms to extract a variant from a string
|
||||
let variant_arms: Vec<_> = enum_def.variants.iter()
|
||||
.enumerate()
|
||||
@@ -572,10 +588,12 @@ fn deserialize_item_enum(
|
||||
impl_generics,
|
||||
ty.clone(),
|
||||
variant,
|
||||
container_attrs,
|
||||
);
|
||||
|
||||
quote_arm!(cx, $variant_name => { $expr })
|
||||
})
|
||||
.chain(ignored_arm.into_iter())
|
||||
.collect();
|
||||
|
||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
||||
@@ -616,6 +634,7 @@ fn deserialize_variant(
|
||||
generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
variant: &ast::Variant,
|
||||
container_attrs: &ContainerAttrs,
|
||||
) -> P<ast::Expr> {
|
||||
let variant_ident = variant.node.name;
|
||||
|
||||
@@ -652,6 +671,7 @@ fn deserialize_variant(
|
||||
generics,
|
||||
ty,
|
||||
fields,
|
||||
container_attrs,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -708,6 +728,7 @@ fn deserialize_struct_variant(
|
||||
generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
fields: &[ast::StructField],
|
||||
container_attrs: &ContainerAttrs,
|
||||
) -> P<ast::Expr> {
|
||||
let where_clause = &generics.where_clause;
|
||||
|
||||
@@ -728,6 +749,7 @@ fn deserialize_struct_variant(
|
||||
builder,
|
||||
type_path,
|
||||
fields,
|
||||
container_attrs,
|
||||
);
|
||||
|
||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
||||
@@ -771,12 +793,20 @@ fn deserialize_field_visitor(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
field_attrs: Vec<attr::FieldAttrs>,
|
||||
container_attrs: &ContainerAttrs,
|
||||
) -> Vec<P<ast::Item>> {
|
||||
// Create the field names for the fields.
|
||||
let field_idents: Vec<ast::Ident> = (0 .. field_attrs.len())
|
||||
.map(|i| builder.id(format!("__field{}", i)))
|
||||
.collect();
|
||||
|
||||
let ignore_variant = if !container_attrs.disallow_unknown() {
|
||||
let skip_ident = builder.id("__ignore");
|
||||
Some(builder.variant(skip_ident).unit())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let field_enum = builder.item()
|
||||
.attr().allow(&["non_camel_case_types"])
|
||||
.enum_("__Field")
|
||||
@@ -785,6 +815,7 @@ fn deserialize_field_visitor(
|
||||
builder.variant(field_ident).unit()
|
||||
})
|
||||
)
|
||||
.with_variants(ignore_variant.into_iter())
|
||||
.build();
|
||||
|
||||
let index_field_arms: Vec<_> = field_idents.iter()
|
||||
@@ -817,12 +848,18 @@ fn deserialize_field_visitor(
|
||||
})
|
||||
.collect();
|
||||
|
||||
let fallthrough_arm_expr = if !container_attrs.disallow_unknown() {
|
||||
quote_expr!(cx, Ok(__Field::__ignore))
|
||||
} else {
|
||||
quote_expr!(cx, Err(::serde::de::Error::unknown_field(value)))
|
||||
};
|
||||
|
||||
let str_body = if formats.is_empty() {
|
||||
// No formats specific attributes, so no match on format required
|
||||
quote_expr!(cx,
|
||||
match value {
|
||||
$default_field_arms
|
||||
_ => { Err(::serde::de::Error::unknown_field(value)) }
|
||||
_ => { $fallthrough_arm_expr }
|
||||
})
|
||||
} else {
|
||||
let field_arms: Vec<_> = formats.iter()
|
||||
@@ -844,7 +881,7 @@ fn deserialize_field_visitor(
|
||||
match value {
|
||||
$arms
|
||||
_ => {
|
||||
Err(::serde::de::Error::unknown_field(value))
|
||||
$fallthrough_arm_expr
|
||||
}
|
||||
}})
|
||||
})
|
||||
@@ -855,7 +892,7 @@ fn deserialize_field_visitor(
|
||||
$fmt_matches
|
||||
_ => match value {
|
||||
$default_field_arms
|
||||
_ => { Err(::serde::de::Error::unknown_field(value)) }
|
||||
_ => $fallthrough_arm_expr
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -920,11 +957,13 @@ fn deserialize_struct_visitor(
|
||||
builder: &aster::AstBuilder,
|
||||
struct_path: ast::Path,
|
||||
fields: &[ast::StructField],
|
||||
container_attrs: &ContainerAttrs,
|
||||
) -> (Vec<P<ast::Item>>, P<ast::Stmt>, P<ast::Expr>) {
|
||||
let field_visitor = deserialize_field_visitor(
|
||||
cx,
|
||||
builder,
|
||||
field::struct_field_attrs(cx, builder, fields),
|
||||
container_attrs
|
||||
);
|
||||
|
||||
let visit_map_expr = deserialize_map(
|
||||
@@ -932,6 +971,7 @@ fn deserialize_struct_visitor(
|
||||
builder,
|
||||
struct_path,
|
||||
fields,
|
||||
container_attrs,
|
||||
);
|
||||
|
||||
let fields_expr = builder.expr().addr_of().slice()
|
||||
@@ -958,6 +998,7 @@ fn deserialize_map(
|
||||
builder: &aster::AstBuilder,
|
||||
struct_path: ast::Path,
|
||||
fields: &[ast::StructField],
|
||||
container_attrs: &ContainerAttrs,
|
||||
) -> P<ast::Expr> {
|
||||
// Create the field names for the fields.
|
||||
let field_names: Vec<ast::Ident> = (0 .. fields.len())
|
||||
@@ -969,6 +1010,16 @@ fn deserialize_map(
|
||||
.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.disallow_unknown() {
|
||||
Some(quote_arm!(cx,
|
||||
_ => { try!(visitor.visit_value::<::serde::de::impls::IgnoredAny>()); }
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Match arms to extract a value for a field.
|
||||
let value_arms: Vec<ast::Arm> = field_names.iter()
|
||||
.map(|field_name| {
|
||||
@@ -978,6 +1029,7 @@ fn deserialize_map(
|
||||
}
|
||||
)
|
||||
})
|
||||
.chain(ignored_arm.into_iter())
|
||||
.collect();
|
||||
|
||||
let extract_values: Vec<P<ast::Stmt>> = field_names.iter()
|
||||
|
||||
@@ -2,7 +2,7 @@ use syntax::ast;
|
||||
use syntax::ext::base::ExtCtxt;
|
||||
|
||||
use aster;
|
||||
use attr::{FieldAttrs, FieldAttrsBuilder};
|
||||
use attr::{ContainerAttrs, ContainerAttrsBuilder, FieldAttrs, FieldAttrsBuilder};
|
||||
|
||||
pub fn struct_field_attrs(
|
||||
_cx: &ExtCtxt,
|
||||
@@ -15,3 +15,10 @@ pub fn struct_field_attrs(
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn container_attrs(
|
||||
_cx: &ExtCtxt,
|
||||
container: &ast::Item,
|
||||
) -> ContainerAttrs {
|
||||
ContainerAttrsBuilder::new().attrs(container.attrs()).build()
|
||||
}
|
||||
Reference in New Issue
Block a user