From ae00c4acb735c8cddf6781ba2bfba2d709fc178c Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 14 Jun 2025 16:30:43 +0500 Subject: [PATCH] Move body generator for internally tagged enums to its own module Cut-paste --- serde_derive/src/de.rs | 91 +--------------------- serde_derive/src/de/enum_internally.rs | 104 +++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 89 deletions(-) create mode 100644 serde_derive/src/de/enum_internally.rs diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index b5c66a47..4632c5ed 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -14,6 +14,7 @@ use syn::{parse_quote, Ident, Index, Member}; mod enum_adjacently; mod enum_externally; +mod enum_internally; pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result { replace_receiver(input); @@ -1262,7 +1263,7 @@ fn deserialize_homogeneous_enum( match cattrs.tag() { attr::TagType::External => enum_externally::deserialize_externally_tagged_enum(params, variants, cattrs), attr::TagType::Internal { tag } => { - deserialize_internally_tagged_enum(params, variants, cattrs, tag) + enum_internally::deserialize_internally_tagged_enum(params, variants, cattrs, tag) } attr::TagType::Adjacent { tag, content } => { enum_adjacently::deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, content) @@ -1313,51 +1314,6 @@ fn prepare_enum_variant_enum(variants: &[Variant]) -> (TokenStream, Stmts) { (variants_stmt, variant_visitor) } -/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` with `#[serde(tag)]` attribute -fn deserialize_internally_tagged_enum( - params: &Parameters, - variants: &[Variant], - cattrs: &attr::Container, - tag: &str, -) -> Fragment { - let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants); - - // Match arms to extract a variant from a string - let variant_arms = variants - .iter() - .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) - .map(|(i, variant)| { - let variant_name = field_i(i); - - let block = Match(deserialize_internally_tagged_variant( - params, variant, cattrs, - )); - - quote! { - __Field::#variant_name => #block - } - }); - - let expecting = format!("internally tagged enum {}", params.type_name()); - let expecting = cattrs.expecting().unwrap_or(&expecting); - - quote_block! { - #variant_visitor - - #variants_stmt - - let (__tag, __content) = _serde::Deserializer::deserialize_any( - __deserializer, - _serde::#private::de::TaggedContentVisitor::<__Field>::new(#tag, #expecting))?; - let __deserializer = _serde::#private::de::ContentDeserializer::<__D::Error>::new(__content); - - match __tag { - #(#variant_arms)* - } - } -} - /// Generates `Deserialize::deserialize` body for an `enum Enum {...}` with `#[serde(untagged)]` attribute fn deserialize_untagged_enum( params: &Parameters, @@ -1398,49 +1354,6 @@ fn deserialize_untagged_enum( } } -// Generates significant part of the visit_seq and visit_map bodies of visitors -// for the variants of internally tagged enum. -fn deserialize_internally_tagged_variant( - params: &Parameters, - variant: &Variant, - cattrs: &attr::Container, -) -> Fragment { - if let Some(path) = variant.attrs.deserialize_with() { - let unwrap_fn = unwrap_to_variant_closure(params, variant, false); - return quote_block! { - _serde::#private::Result::map(#path(__deserializer), #unwrap_fn) - }; - } - - let variant_ident = &variant.ident; - - match effective_style(variant) { - Style::Unit => { - let this_value = ¶ms.this_value; - let type_name = params.type_name(); - let variant_name = variant.ident.to_string(); - let default = variant.fields.first().map(|field| { - let default = Expr(expr_is_missing(field, cattrs)); - quote!((#default)) - }); - quote_block! { - _serde::Deserializer::deserialize_any(__deserializer, _serde::#private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name))?; - _serde::#private::Ok(#this_value::#variant_ident #default) - } - } - Style::Newtype => { - deserialize_untagged_newtype_variant(variant_ident, params, &variant.fields[0]) - } - Style::Struct => deserialize_struct( - params, - &variant.fields, - cattrs, - StructForm::InternallyTagged(variant_ident), - ), - Style::Tuple => unreachable!("checked in serde_derive_internals"), - } -} - fn deserialize_untagged_variant( params: &Parameters, variant: &Variant, diff --git a/serde_derive/src/de/enum_internally.rs b/serde_derive/src/de/enum_internally.rs new file mode 100644 index 00000000..8c848e22 --- /dev/null +++ b/serde_derive/src/de/enum_internally.rs @@ -0,0 +1,104 @@ +//! Generator of the deserialization code for the internally tagged enums: +//! +//! ```ignore +//! #[serde(tag = "...")] +//! enum Enum {} +//! ``` + +use crate::de::{ + deserialize_struct, deserialize_untagged_newtype_variant, effective_style, expr_is_missing, + field_i, prepare_enum_variant_enum, unwrap_to_variant_closure, Parameters, StructForm, +}; +use crate::fragment::{Expr, Fragment, Match}; +use crate::internals::ast::{Style, Variant}; +use crate::internals::attr; +use crate::private; +use quote::quote; + +/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` with `#[serde(tag)]` attribute +pub fn deserialize_internally_tagged_enum( + params: &Parameters, + variants: &[Variant], + cattrs: &attr::Container, + tag: &str, +) -> Fragment { + let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants); + + // Match arms to extract a variant from a string + let variant_arms = variants + .iter() + .enumerate() + .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) + .map(|(i, variant)| { + let variant_name = field_i(i); + + let block = Match(deserialize_internally_tagged_variant( + params, variant, cattrs, + )); + + quote! { + __Field::#variant_name => #block + } + }); + + let expecting = format!("internally tagged enum {}", params.type_name()); + let expecting = cattrs.expecting().unwrap_or(&expecting); + + quote_block! { + #variant_visitor + + #variants_stmt + + let (__tag, __content) = _serde::Deserializer::deserialize_any( + __deserializer, + _serde::#private::de::TaggedContentVisitor::<__Field>::new(#tag, #expecting))?; + let __deserializer = _serde::#private::de::ContentDeserializer::<__D::Error>::new(__content); + + match __tag { + #(#variant_arms)* + } + } +} + +// Generates significant part of the visit_seq and visit_map bodies of visitors +// for the variants of internally tagged enum. +fn deserialize_internally_tagged_variant( + params: &Parameters, + variant: &Variant, + cattrs: &attr::Container, +) -> Fragment { + if let Some(path) = variant.attrs.deserialize_with() { + let unwrap_fn = unwrap_to_variant_closure(params, variant, false); + return quote_block! { + _serde::#private::Result::map(#path(__deserializer), #unwrap_fn) + }; + } + + let variant_ident = &variant.ident; + + match effective_style(variant) { + Style::Unit => { + let this_value = ¶ms.this_value; + let type_name = params.type_name(); + let variant_name = variant.ident.to_string(); + let default = variant.fields.first().map(|field| { + let default = Expr(expr_is_missing(field, cattrs)); + quote!((#default)) + }); + quote_block! { + _serde::Deserializer::deserialize_any(__deserializer, _serde::#private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name))?; + _serde::#private::Ok(#this_value::#variant_ident #default) + } + } + Style::Newtype => { + deserialize_untagged_newtype_variant(variant_ident, params, &variant.fields[0]) + } + Style::Struct => deserialize_struct( + params, + &variant.fields, + cattrs, + StructForm::InternallyTagged(variant_ident), + ), + Style::Tuple => unreachable!("checked in serde_derive_internals"), + } +}