Implement deserializer for map mode and collection fields

This commit is contained in:
Armin Ronacher 2018-03-14 14:15:45 +01:00
parent b4dbae250b
commit 39413c8ce7
2 changed files with 96 additions and 25 deletions

View File

@ -13,6 +13,7 @@ pub use lib::fmt::{self, Formatter};
pub use lib::marker::PhantomData;
pub use lib::option::Option::{self, None, Some};
pub use lib::result::Result::{self, Err, Ok};
pub use lib::iter::once;
pub use self::string::from_utf8_lossy;

View File

@ -766,6 +766,10 @@ fn deserialize_struct(
untagged: &Untagged,
) -> Fragment {
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 (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
@ -793,10 +797,13 @@ fn deserialize_struct(
let visit_seq = Stmts(deserialize_seq(&type_path, params, fields, true, cattrs));
let (field_visitor, fields_stmt, visit_map) =
deserialize_struct_visitor(&type_path, params, fields, cattrs);
let (field_visitor, fields_stmt, visit_map) = if as_map {
deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs)
} else {
deserialize_struct_as_struct_visitor(&type_path, params, fields, cattrs)
};
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 visitor_expr = quote! {
@ -813,6 +820,10 @@ fn deserialize_struct(
quote! {
_serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
}
} else if as_map {
quote! {
_serde::Deserializer::deserialize_map(__deserializer, #visitor_expr)
}
} else {
let type_name = cattrs.name().deserialize_name();
quote! {
@ -827,10 +838,10 @@ fn deserialize_struct(
quote!(mut __seq)
};
// untagged struct variants do not get a visit_seq method
// untagged struct variants do not get a visit_seq method. The same applies to structs that
// only have a map representation.
let visit_seq = match *untagged {
Untagged::Yes => None,
Untagged::No => Some(quote! {
Untagged::No if !as_map => Some(quote! {
#[inline]
fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::SeqAccess<#delife>
@ -838,6 +849,7 @@ fn deserialize_struct(
#visit_seq
}
}),
_ => None,
};
quote_block! {
@ -1021,6 +1033,7 @@ fn deserialize_externally_tagged_enum(
&variant_names_idents,
cattrs,
true,
false,
));
// Match arms to extract a variant from a string
@ -1120,6 +1133,7 @@ fn deserialize_internally_tagged_enum(
&variant_names_idents,
cattrs,
true,
false,
));
// Match arms to extract a variant from a string
@ -1189,6 +1203,7 @@ fn deserialize_adjacently_tagged_enum(
&variant_names_idents,
cattrs,
true,
false,
));
let variant_arms: &Vec<_> = &variants
@ -1689,12 +1704,17 @@ fn deserialize_generated_identifier(
fields: &[(String, Ident)],
cattrs: &attr::Container,
is_variant: bool,
struct_as_map_mode: bool,
) -> Fragment {
let this = quote!(__Field);
let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident)| ident).collect();
let (ignore_variant, fallthrough) = if is_variant || cattrs.deny_unknown_fields() {
(None, None)
} else if cattrs.unknown_fields_into().is_some() {
let ignore_variant = quote!(__other(String),);
let fallthrough = quote!(_serde::export::Ok(__Field::__other(__value.to_string())));
(Some(ignore_variant), Some(fallthrough))
} else {
let ignore_variant = quote!(__ignore,);
let fallthrough = quote!(_serde::export::Ok(__Field::__ignore));
@ -1706,6 +1726,7 @@ fn deserialize_generated_identifier(
fields,
is_variant,
fallthrough,
struct_as_map_mode,
));
quote_block! {
@ -1804,6 +1825,7 @@ fn deserialize_custom_identifier(
&names_idents,
is_variant,
fallthrough,
false,
));
quote_block! {
@ -1833,6 +1855,7 @@ fn deserialize_identifier(
fields: &[(String, Ident)],
is_variant: bool,
fallthrough: Option<Tokens>,
struct_as_map_mode: bool,
) -> Fragment {
let field_strs = fields.iter().map(|&(ref name, _)| name);
let field_bytes = fields.iter().map(|&(ref name, _)| Literal::byte_string(name.as_bytes()));
@ -1852,22 +1875,26 @@ fn deserialize_identifier(
let variant_indices = 0u64..;
let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len());
let visit_index = quote! {
fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
where __E: _serde::de::Error
{
match __value {
#(
#variant_indices => _serde::export::Ok(#constructors),
)*
_ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value),
&#fallthrough_msg))
let visit_index = if struct_as_map_mode {
None
} else {
Some(quote! {
fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
where __E: _serde::de::Error
{
match __value {
#(
#variant_indices => _serde::export::Ok(#constructors),
)*
_ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value),
&#fallthrough_msg))
}
}
}
})
};
let bytes_to_str = if fallthrough.is_some() {
let bytes_to_str = if fallthrough.is_some() && !struct_as_map_mode {
None
} else {
let conversion = quote! {
@ -1922,12 +1949,12 @@ fn deserialize_identifier(
}
}
fn deserialize_struct_visitor(
fn deserialize_struct_as_struct_visitor(
struct_path: &Tokens,
params: &Parameters,
fields: &[Field],
cattrs: &attr::Container,
) -> (Fragment, Fragment, Fragment) {
) -> (Fragment, Option<Fragment>, Fragment) {
let field_names_idents: Vec<_> = fields
.iter()
.enumerate()
@ -1942,11 +1969,31 @@ fn deserialize_struct_visitor(
}
};
let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false);
let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, false);
let visit_map = deserialize_map(struct_path, params, fields, cattrs);
(field_visitor, fields_stmt, visit_map)
(field_visitor, Some(fields_stmt), visit_map)
}
fn deserialize_struct_as_map_visitor(
struct_path: &Tokens,
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(struct_path, params, fields, cattrs);
(field_visitor, None, visit_map)
}
fn deserialize_map(
@ -1973,6 +2020,19 @@ fn deserialize_map(
}
});
// If we have a collect into, we also need a container that can
// hold the data we accumulate.
let mut let_collect = None;
for &(field, _) in fields_names.iter() {
if field.attrs.collection_field() {
let field_ty = &field.ty;
let_collect = Some(quote! {
let mut __collect: #field_ty = Default::default();
});
break;
}
}
// Match arms to extract a value for a field.
let value_arms = fields_names
.iter()
@ -2010,6 +2070,12 @@ fn deserialize_map(
// Visit ignored values to consume them
let ignored_arm = if cattrs.deny_unknown_fields() {
None
} else if cattrs.unknown_fields_into().is_some() {
Some(quote! {
__Field::__other(__name) => {
__collect.extend(_serde::export::once((__name, try!(_serde::de::MapAccess::next_value(&mut __map)))));
}
})
} else {
Some(quote! {
_ => { let _ = try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)); }
@ -2052,7 +2118,9 @@ fn deserialize_map(
let result = fields_names.iter().map(|&(field, ref name)| {
let ident = field.ident.expect("struct contains unnamed fields");
if field.attrs.skip_deserializing() {
if field.attrs.collection_field() {
quote_spanned!(Span::call_site()=> #ident: __collect)
} else if field.attrs.skip_deserializing() {
let value = Expr(expr_is_missing(field, cattrs));
quote_spanned!(Span::call_site()=> #ident: #value)
} else {
@ -2085,6 +2153,8 @@ fn deserialize_map(
quote_block! {
#(#let_values)*
#let_collect
#match_keys
#let_default
@ -2115,7 +2185,7 @@ fn deserialize_struct_in_place_visitor(
}
};
let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false);
let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, false);
let visit_map = deserialize_map_in_place(params, fields, cattrs);