Skip to content

Commit

Permalink
Merge pull request #2443 from Mingun/deserialize-in-place
Browse files Browse the repository at this point in the history
Simplify code in deserialize_in_place_struct and implement #2387 for in-place case
  • Loading branch information
dtolnay authored Aug 2, 2023
2 parents 0c36783 + 878110a commit 83b1a3d
Showing 1 changed file with 58 additions and 124 deletions.
182 changes: 58 additions & 124 deletions serde_derive/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,10 @@ fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option<St

let code = match &cont.data {
Data::Struct(Style::Struct, fields) => {
deserialize_struct_in_place(None, params, fields, &cont.attrs, None)?
deserialize_struct_in_place(params, fields, &cont.attrs)?
}
Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => {
deserialize_tuple_in_place(None, params, fields, &cont.attrs, None)
deserialize_tuple_in_place(params, fields, &cont.attrs)
}
Data::Enum(_) | Data::Struct(Style::Unit, _) => {
return None;
Expand Down Expand Up @@ -582,11 +582,9 @@ fn deserialize_tuple(

#[cfg(feature = "deserialize_in_place")]
fn deserialize_tuple_in_place(
variant_ident: Option<syn::Ident>,
params: &Parameters,
fields: &[Field],
cattrs: &attr::Container,
deserializer: Option<TokenStream>,
) -> Fragment {
assert!(!cattrs.has_flatten());

Expand All @@ -600,17 +598,25 @@ fn deserialize_tuple_in_place(
split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime();

let is_enum = variant_ident.is_some();
let expecting = match variant_ident {
Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
None => format!("tuple struct {}", params.type_name()),
};
let expecting = format!("tuple struct {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);

let nfields = fields.len();

let visit_newtype_struct = if !is_enum && nfields == 1 {
Some(deserialize_newtype_struct_in_place(params, &fields[0]))
let visit_newtype_struct = if nfields == 1 {
// We do not generate deserialize_in_place if every field has a
// deserialize_with.
assert!(fields[0].attrs.deserialize_with().is_none());

Some(quote! {
#[inline]
fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::__private::Result<Self::Value, __E::Error>
where
__E: _serde::Deserializer<#delife>,
{
_serde::Deserialize::deserialize_in_place(__e, &mut self.place.0)
}
})
} else {
None
};
Expand All @@ -624,15 +630,10 @@ fn deserialize_tuple_in_place(
}
};

let dispatch = if let Some(deserializer) = deserializer {
quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #field_count, #visitor_expr))
} else if is_enum {
quote!(_serde::de::VariantAccess::tuple_variant(__variant, #field_count, #visitor_expr))
} else if nfields == 1 {
let type_name = cattrs.name().deserialize_name();
let type_name = cattrs.name().deserialize_name();
let dispatch = if nfields == 1 {
quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr))
} else {
let type_name = cattrs.name().deserialize_name();
quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr))
};

Expand Down Expand Up @@ -918,25 +919,6 @@ fn deserialize_newtype_struct(
}
}

#[cfg(feature = "deserialize_in_place")]
fn deserialize_newtype_struct_in_place(params: &Parameters, field: &Field) -> TokenStream {
// We do not generate deserialize_in_place if every field has a
// deserialize_with.
assert!(field.attrs.deserialize_with().is_none());

let delife = params.borrowed.de_lifetime();

quote! {
#[inline]
fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::__private::Result<Self::Value, __E::Error>
where
__E: _serde::Deserializer<#delife>,
{
_serde::Deserialize::deserialize_in_place(__e, &mut self.place.0)
}
}
}

enum StructForm<'a> {
Struct,
/// Contains a variant name
Expand Down Expand Up @@ -1133,14 +1115,10 @@ fn deserialize_struct(

#[cfg(feature = "deserialize_in_place")]
fn deserialize_struct_in_place(
variant_ident: Option<syn::Ident>,
params: &Parameters,
fields: &[Field],
cattrs: &attr::Container,
deserializer: Option<TokenStream>,
) -> Option<Fragment> {
let is_enum = variant_ident.is_some();

// for now we do not support in_place deserialization for structs that
// are represented as map.
if cattrs.has_flatten() {
Expand All @@ -1152,58 +1130,40 @@ fn deserialize_struct_in_place(
split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime();

let expecting = match variant_ident {
Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
None => format!("struct {}", params.type_name()),
};
let expecting = format!("struct {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);

let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, expecting));

let (field_visitor, fields_stmt, visit_map) =
deserialize_struct_as_struct_in_place_visitor(params, fields, cattrs);

let field_visitor = Stmts(field_visitor);
let fields_stmt = Stmts(fields_stmt);
let visit_map = Stmts(visit_map);
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),
field.attrs.aliases(),
)
})
.collect();

let visitor_expr = quote! {
__Visitor {
place: __place,
lifetime: _serde::__private::PhantomData,
}
};
let dispatch = if let Some(deserializer) = deserializer {
quote! {
_serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
}
} else if is_enum {
quote! {
_serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
}
} else {
let type_name = cattrs.name().deserialize_name();
quote! {
_serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr)
}
};
let field_visitor = Stmts(deserialize_generated_identifier(
&field_names_idents,
cattrs,
false,
None,
));

let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
let visitor_var = if all_skipped {
let mut_seq = if field_names_idents.is_empty() {
quote!(_)
} else {
quote!(mut __seq)
};

let visit_seq = quote! {
#[inline]
fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::__private::Result<Self::Value, __A::Error>
where
__A: _serde::de::SeqAccess<#delife>,
{
#visit_seq
}
};
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, expecting));
let visit_map = Stmts(deserialize_map_in_place(params, fields, cattrs));
let field_names = field_names_idents
.iter()
.flat_map(|(_, _, aliases)| aliases);
let type_name = cattrs.name().deserialize_name();

let in_place_impl_generics = de_impl_generics.in_place();
let in_place_ty_generics = de_ty_generics.in_place();
Expand All @@ -1225,7 +1185,13 @@ fn deserialize_struct_in_place(
_serde::__private::Formatter::write_str(__formatter, #expecting)
}

#visit_seq
#[inline]
fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result<Self::Value, __A::Error>
where
__A: _serde::de::SeqAccess<#delife>,
{
#visit_seq
}

#[inline]
fn visit_map<__A>(self, mut __map: __A) -> _serde::__private::Result<Self::Value, __A::Error>
Expand All @@ -1236,9 +1202,13 @@ fn deserialize_struct_in_place(
}
}

#fields_stmt
#[doc(hidden)]
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];

#dispatch
_serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, __Visitor {
place: __place,
lifetime: _serde::__private::PhantomData,
})
})
}

Expand Down Expand Up @@ -2707,42 +2677,6 @@ fn deserialize_map(
}
}

#[cfg(feature = "deserialize_in_place")]
fn deserialize_struct_as_struct_in_place_visitor(
params: &Parameters,
fields: &[Field],
cattrs: &attr::Container,
) -> (Fragment, Fragment, Fragment) {
assert!(!cattrs.has_flatten());

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),
field.attrs.aliases(),
)
})
.collect();

let fields_stmt = {
let field_names = field_names_idents.iter().map(|(name, _, _)| name);
quote_block! {
#[doc(hidden)]
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
}
};

let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);

let visit_map = deserialize_map_in_place(params, fields, cattrs);

(field_visitor, fields_stmt, visit_map)
}

#[cfg(feature = "deserialize_in_place")]
fn deserialize_map_in_place(
params: &Parameters,
Expand Down

0 comments on commit 83b1a3d

Please sign in to comment.