mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-06-17 15:01:03 +00:00
Attempted support for in_place deserialization for structs as map
This commit is contained in:
@@ -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
@@ -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 = ¶ms.this;
|
let this = ¶ms.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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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`",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user