serde_derive/de/
enum_internally.rs

1//! Deserialization for internally tagged enums:
2//!
3//! ```ignore
4//! #[serde(tag = "...")]
5//! enum Enum {}
6//! ```
7
8use crate::de::enum_;
9use crate::de::enum_untagged;
10use crate::de::struct_;
11use crate::de::{
12    effective_style, expr_is_missing, field_i, unwrap_to_variant_closure, Parameters, StructForm,
13};
14use crate::fragment::{Expr, Fragment, Match};
15use crate::internals::ast::{Style, Variant};
16use crate::internals::attr;
17use crate::private;
18use quote::quote;
19
20/// Generates `Deserialize::deserialize` body for an `enum Enum {...}` with `#[serde(tag)]` attribute
21pub(super) fn deserialize(
22    params: &Parameters,
23    variants: &[Variant],
24    cattrs: &attr::Container,
25    tag: &str,
26) -> Fragment {
27    let (variants_stmt, variant_visitor) = enum_::prepare_enum_variant_enum(variants);
28
29    // Match arms to extract a variant from a string
30    let variant_arms = variants
31        .iter()
32        .enumerate()
33        .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
34        .map(|(i, variant)| {
35            let variant_name = field_i(i);
36
37            let block = Match(deserialize_internally_tagged_variant(
38                params, variant, cattrs,
39            ));
40
41            quote! {
42                __Field::#variant_name => #block
43            }
44        });
45
46    let expecting = format!("internally tagged enum {}", params.type_name());
47    let expecting = cattrs.expecting().unwrap_or(&expecting);
48
49    quote_block! {
50        #variant_visitor
51
52        #variants_stmt
53
54        let (__tag, __content) = _serde::Deserializer::deserialize_any(
55            __deserializer,
56            _serde::#private::de::TaggedContentVisitor::<__Field>::new(#tag, #expecting))?;
57        let __deserializer = _serde::#private::de::ContentDeserializer::<__D::Error>::new(__content);
58
59        match __tag {
60            #(#variant_arms)*
61        }
62    }
63}
64
65// Generates significant part of the visit_seq and visit_map bodies of visitors
66// for the variants of internally tagged enum.
67fn deserialize_internally_tagged_variant(
68    params: &Parameters,
69    variant: &Variant,
70    cattrs: &attr::Container,
71) -> Fragment {
72    if let Some(path) = variant.attrs.deserialize_with() {
73        let unwrap_fn = unwrap_to_variant_closure(params, variant, false);
74        return quote_block! {
75            _serde::#private::Result::map(#path(__deserializer), #unwrap_fn)
76        };
77    }
78
79    let variant_ident = &variant.ident;
80
81    match effective_style(variant) {
82        Style::Unit => {
83            let this_value = &params.this_value;
84            let type_name = params.type_name();
85            let variant_name = variant.ident.to_string();
86            let default = variant.fields.first().map(|field| {
87                let default = Expr(expr_is_missing(field, cattrs));
88                quote!((#default))
89            });
90            quote_block! {
91                _serde::Deserializer::deserialize_any(__deserializer, _serde::#private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name))?;
92                _serde::#private::Ok(#this_value::#variant_ident #default)
93            }
94        }
95        Style::Newtype => {
96            enum_untagged::deserialize_newtype_variant(variant_ident, params, &variant.fields[0])
97        }
98        Style::Struct => struct_::deserialize(
99            params,
100            &variant.fields,
101            cattrs,
102            StructForm::InternallyTagged(variant_ident),
103        ),
104        Style::Tuple => unreachable!("checked in serde_derive_internals"),
105    }
106}