mirror of
https://github.com/serde-rs/serde.git
synced 2025-09-27 13:00:49 +00:00
107 lines
3.5 KiB
Rust
107 lines
3.5 KiB
Rust
//! Generator of the deserialization code for the internally tagged enums:
|
|
//!
|
|
//! ```ignore
|
|
//! #[serde(tag = "...")]
|
|
//! enum Enum {}
|
|
//! ```
|
|
|
|
use crate::de::enum_;
|
|
use crate::de::enum_untagged;
|
|
use crate::de::struct_;
|
|
use crate::de::{
|
|
effective_style, expr_is_missing, field_i, 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(super) fn generate_body(
|
|
params: &Parameters,
|
|
variants: &[Variant],
|
|
cattrs: &attr::Container,
|
|
tag: &str,
|
|
) -> Fragment {
|
|
let (variants_stmt, variant_visitor) = enum_::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 => {
|
|
enum_untagged::generate_newtype_variant(variant_ident, params, &variant.fields[0])
|
|
}
|
|
Style::Struct => struct_::generate_body(
|
|
params,
|
|
&variant.fields,
|
|
cattrs,
|
|
StructForm::InternallyTagged(variant_ident),
|
|
),
|
|
Style::Tuple => unreachable!("checked in serde_derive_internals"),
|
|
}
|
|
}
|