serde_derive/de/
enum_.rs

1use crate::de::enum_adjacently;
2use crate::de::enum_externally;
3use crate::de::enum_internally;
4use crate::de::enum_untagged;
5use crate::de::identifier;
6use crate::de::{field_i, FieldWithAliases, Parameters};
7use crate::fragment::{Expr, Fragment, Stmts};
8use crate::internals::ast::Variant;
9use crate::internals::attr;
10use crate::private;
11use proc_macro2::TokenStream;
12use quote::quote;
13
14/// Generates `Deserialize::deserialize` body for an `enum Enum {...}`
15pub(super) fn deserialize(
16    params: &Parameters,
17    variants: &[Variant],
18    cattrs: &attr::Container,
19) -> Fragment {
20    // The variants have already been checked (in ast.rs) that all untagged variants appear at the end
21    match variants.iter().position(|var| var.attrs.untagged()) {
22        Some(variant_idx) => {
23            let (tagged, untagged) = variants.split_at(variant_idx);
24            let tagged_frag = Expr(deserialize_homogeneous_enum(params, tagged, cattrs));
25            // Ignore any error associated with non-untagged deserialization so that we
26            // can fall through to the untagged variants. This may be infallible so we
27            // need to provide the error type.
28            let first_attempt = quote! {
29                if let _serde::#private::Result::<_, __D::Error>::Ok(__ok) = (|| #tagged_frag)() {
30                    return _serde::#private::Ok(__ok);
31                }
32            };
33            enum_untagged::deserialize(params, untagged, cattrs, Some(first_attempt))
34        }
35        None => deserialize_homogeneous_enum(params, variants, cattrs),
36    }
37}
38
39fn deserialize_homogeneous_enum(
40    params: &Parameters,
41    variants: &[Variant],
42    cattrs: &attr::Container,
43) -> Fragment {
44    match cattrs.tag() {
45        attr::TagType::External => enum_externally::deserialize(params, variants, cattrs),
46        attr::TagType::Internal { tag } => {
47            enum_internally::deserialize(params, variants, cattrs, tag)
48        }
49        attr::TagType::Adjacent { tag, content } => {
50            enum_adjacently::deserialize(params, variants, cattrs, tag, content)
51        }
52        attr::TagType::None => enum_untagged::deserialize(params, variants, cattrs, None),
53    }
54}
55
56pub fn prepare_enum_variant_enum(variants: &[Variant]) -> (TokenStream, Stmts) {
57    let deserialized_variants = variants
58        .iter()
59        .enumerate()
60        .filter(|&(_i, variant)| !variant.attrs.skip_deserializing());
61
62    let fallthrough = deserialized_variants
63        .clone()
64        .find(|(_i, variant)| variant.attrs.other())
65        .map(|(i, _variant)| {
66            let ignore_variant = field_i(i);
67            quote!(_serde::#private::Ok(__Field::#ignore_variant))
68        });
69
70    let variants_stmt = {
71        let variant_names = deserialized_variants
72            .clone()
73            .flat_map(|(_i, variant)| variant.attrs.aliases());
74        quote! {
75            #[doc(hidden)]
76            const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
77        }
78    };
79
80    let deserialized_variants: Vec<_> = deserialized_variants
81        .map(|(i, variant)| FieldWithAliases {
82            ident: field_i(i),
83            aliases: variant.attrs.aliases(),
84        })
85        .collect();
86
87    let variant_visitor = Stmts(identifier::deserialize_generated(
88        &deserialized_variants,
89        false, // variant identifiers do not depend on the presence of flatten fields
90        true,
91        None,
92        fallthrough,
93    ));
94
95    (variants_stmt, variant_visitor)
96}