Attempted support for in_place deserialization for structs as map

This commit is contained in:
Armin Ronacher
2018-03-18 18:22:06 +01:00
parent f1af2dc5ab
commit 61b167be9a
3 changed files with 138 additions and 26 deletions
-7
View File
@@ -255,13 +255,6 @@ macro_rules! declare_error_trait {
} }
} }
/// Raised when a `Deserialize` struct type recieved a field with an
/// unrecognized name but the names are not actually known because of
/// flattening.
fn unknown_field_in_flattened_structure(field: &str) -> Self {
Error::custom(format_args!("unknown field `{}`", field))
}
/// Raised when a `Deserialize` struct type expected to receive a required /// Raised when a `Deserialize` struct type expected to receive a required
/// field with a particular name but that field was not present in the /// field with a particular name but that field was not present in the
/// input. /// input.
+100 -19
View File
@@ -831,7 +831,7 @@ fn deserialize_struct(
} }
}; };
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); let all_skipped = !cattrs.has_flatten() && fields.iter().all(|field| field.attrs.skip_deserializing());
let visitor_var = if all_skipped { let visitor_var = if all_skipped {
quote!(_) quote!(_)
} else { } else {
@@ -892,6 +892,10 @@ fn deserialize_struct_in_place(
deserializer: Option<Tokens>, deserializer: Option<Tokens>,
) -> Fragment { ) -> Fragment {
let is_enum = variant_ident.is_some(); let is_enum = variant_ident.is_some();
let as_map = deserializer.is_none() && !is_enum && match cattrs.repr() {
attr::ContainerRepr::Struct | attr::ContainerRepr::Auto => false,
attr::ContainerRepr::Map => true,
};
let this = &params.this; let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
@@ -905,10 +909,14 @@ fn deserialize_struct_in_place(
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs)); let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs));
let (field_visitor, fields_stmt, visit_map) = let (field_visitor, fields_stmt, visit_map) = if as_map {
deserialize_struct_in_place_visitor(params, fields, cattrs); deserialize_struct_as_map_in_place_visitor(params, fields, cattrs)
} else {
deserialize_struct_as_struct_in_place_visitor(params, fields, cattrs)
};
let field_visitor = Stmts(field_visitor); let field_visitor = Stmts(field_visitor);
let fields_stmt = Stmts(fields_stmt); let fields_stmt = fields_stmt.map(Stmts);
let visit_map = Stmts(visit_map); let visit_map = Stmts(visit_map);
let visitor_expr = quote! { let visitor_expr = quote! {
@@ -925,6 +933,10 @@ fn deserialize_struct_in_place(
quote! { quote! {
_serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
} }
} else if as_map {
quote! {
_serde::Deserializer::deserialize_map(__deserializer, #visitor_expr)
}
} else { } else {
let type_name = cattrs.name().deserialize_name(); let type_name = cattrs.name().deserialize_name();
quote! { quote! {
@@ -932,20 +944,24 @@ fn deserialize_struct_in_place(
} }
}; };
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); let all_skipped = !cattrs.has_flatten() && fields.iter().all(|field| field.attrs.skip_deserializing());
let visitor_var = if all_skipped { let visitor_var = if all_skipped {
quote!(_) quote!(_)
} else { } else {
quote!(mut __seq) quote!(mut __seq)
}; };
let visit_seq = quote! { let visit_seq = if as_map {
#[inline] None
fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error> } else {
where __A: _serde::de::SeqAccess<#delife> Some(quote! {
{ #[inline]
#visit_seq fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
} where __A: _serde::de::SeqAccess<#delife>
{
#visit_seq
}
})
}; };
let in_place_impl_generics = de_impl_generics.in_place(); let in_place_impl_generics = de_impl_generics.in_place();
@@ -2081,7 +2097,7 @@ fn deserialize_map(
}) })
}; };
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); let all_skipped = !cattrs.has_flatten() && fields.iter().all(|field| field.attrs.skip_deserializing());
let match_keys = if cattrs.deny_unknown_fields() && all_skipped { let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
quote! { quote! {
// FIXME: Once we drop support for Rust 1.15: // FIXME: Once we drop support for Rust 1.15:
@@ -2132,7 +2148,7 @@ fn deserialize_map(
Some(quote! { Some(quote! {
if let Some(Some((__key, _))) = __collect.into_iter().filter(|x| x.is_some()).next() { if let Some(Some((__key, _))) = __collect.into_iter().filter(|x| x.is_some()).next() {
return _serde::export::Err( return _serde::export::Err(
_serde::de::Error::unknown_field_in_flattened_structure(&__key)); _serde::de::Error::custom(format_args!("unknown field `{}`", &__key)));
} }
}) })
} else { } else {
@@ -2191,11 +2207,11 @@ fn deserialize_map(
} }
#[cfg(feature = "deserialize_in_place")] #[cfg(feature = "deserialize_in_place")]
fn deserialize_struct_in_place_visitor( fn deserialize_struct_as_struct_in_place_visitor(
params: &Parameters, params: &Parameters,
fields: &[Field], fields: &[Field],
cattrs: &attr::Container, cattrs: &attr::Container,
) -> (Fragment, Fragment, Fragment) { ) -> (Fragment, Option<Fragment>, Fragment) {
let field_names_idents: Vec<_> = fields let field_names_idents: Vec<_> = fields
.iter() .iter()
.enumerate() .enumerate()
@@ -2214,7 +2230,27 @@ fn deserialize_struct_in_place_visitor(
let visit_map = deserialize_map_in_place(params, fields, cattrs); let visit_map = deserialize_map_in_place(params, fields, cattrs);
(field_visitor, fields_stmt, visit_map) (field_visitor, Some(fields_stmt), visit_map)
}
#[cfg(feature = "deserialize_in_place")]
fn deserialize_struct_as_map_in_place_visitor(
params: &Parameters,
fields: &[Field],
cattrs: &attr::Container,
) -> (Fragment, Option<Fragment>, Fragment) {
let field_names_idents: Vec<_> = fields
.iter()
.enumerate()
.filter(|&(_, field)| !field.attrs.skip_deserializing())
.map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i)))
.collect();
let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, true);
let visit_map = deserialize_map_in_place(params, fields, cattrs);
(field_visitor, None, visit_map)
} }
#[cfg(feature = "deserialize_in_place")] #[cfg(feature = "deserialize_in_place")]
@@ -2240,6 +2276,15 @@ fn deserialize_map_in_place(
} }
}); });
// Collect contents for flatten fields into a buffer
let let_collect = if cattrs.has_flatten() {
Some(quote! {
let mut __collect = Vec::<Option<(String, _serde::private::de::Content)>>::new();
})
} else {
None
};
// Match arms to extract a value for a field. // Match arms to extract a value for a field.
let value_arms_from = fields_names let value_arms_from = fields_names
.iter() .iter()
@@ -2274,7 +2319,13 @@ fn deserialize_map_in_place(
}); });
// Visit ignored values to consume them // Visit ignored values to consume them
let ignored_arm = if cattrs.deny_unknown_fields() { let ignored_arm = if cattrs.has_flatten() {
Some(quote! {
__Field::__other(__name) => {
__collect.push(Some((__name, try!(_serde::de::MapAccess::next_value(&mut __map)))));
}
})
} else if cattrs.deny_unknown_fields() {
None None
} else { } else {
Some(quote! { Some(quote! {
@@ -2282,7 +2333,7 @@ fn deserialize_map_in_place(
}) })
}; };
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); let all_skipped = !cattrs.has_flatten() && fields.iter().all(|field| field.attrs.skip_deserializing());
let match_keys = if cattrs.deny_unknown_fields() && all_skipped { let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
quote! { quote! {
@@ -2346,15 +2397,45 @@ fn deserialize_map_in_place(
} }
}; };
let extract_collected = fields_names
.iter()
.filter(|&&(field, _)| field.attrs.flatten())
.map(|&(field, ref name)| {
let field_ty = field.ty;
quote! {
let #name: #field_ty = try!(_serde::de::Deserialize::deserialize(
_serde::private::de::FlatMapDeserializer(
&mut __collect,
_serde::export::PhantomData)));
}
});
let collected_deny_unknown_fields = if cattrs.has_flatten() && cattrs.deny_unknown_fields() {
Some(quote! {
if let Some(Some((__key, _))) = __collect.into_iter().filter(|x| x.is_some()).next() {
return _serde::export::Err(
_serde::de::Error::custom(format_args!("unknown field `{}`", &__key)));
}
})
} else {
None
};
quote_block! { quote_block! {
#(#let_flags)* #(#let_flags)*
#let_collect
#match_keys #match_keys
#let_default #let_default
#(#check_flags)* #(#check_flags)*
#(#extract_collected)*
#collected_deny_unknown_fields
_serde::export::Ok(()) _serde::export::Ok(())
} }
} }
+38
View File
@@ -1493,3 +1493,41 @@ fn test_flatten_struct_tag_content_enum_newtype() {
], ],
); );
} }
#[test]
fn test_unknown_field_in_flatten() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(repr = "map", deny_unknown_fields)]
struct Outer {
dummy: String,
#[serde(flatten)]
inner: Inner,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Inner {
foo: HashMap<String, u32>,
}
assert_de_tokens_error::<Outer>(
&[
Token::Struct {
name: "Outer",
len: 1,
},
Token::Str("dummy"),
Token::Str("23"),
Token::Str("foo"),
Token::Map { len: None },
Token::Str("a"),
Token::U32(1),
Token::Str("b"),
Token::U32(2),
Token::MapEnd,
Token::Str("bar"),
Token::U32(23),
Token::StructEnd,
],
"unknown field `bar`",
);
}