more_convert_derive_internal/enum_repr/
mod.rs

1use crate::require_enum;
2use enum_arg::EnumReprArg;
3use proc_macro2::TokenStream;
4use syn::{spanned::Spanned, Expr, Ident};
5use variant_arg::EnumReprVariant;
6
7mod enum_arg;
8mod internal;
9mod variant_arg;
10
11pub(crate) struct FinalVariantData<'a> {
12    pub ident: &'a Ident,
13    pub discriminant: Expr,
14}
15
16pub fn derive_enum_repr(input: syn::DeriveInput) -> syn::Result<TokenStream> {
17    let variants = require_enum(&input)?;
18    let mut repr: Option<TokenStream> = None;
19    let mut enum_arg: Option<EnumReprArg> = None;
20    for attr in &input.attrs {
21        match attr.path().get_ident() {
22            Some(ident) if ident == "repr" => {
23                repr = Some(attr.parse_args()?);
24            }
25            Some(ident) if ident == "enum_repr" => {
26                enum_arg = Some(attr.parse_args()?);
27            }
28            _ => {}
29        }
30    }
31
32    let repr = repr.ok_or_else(|| syn::Error::new(input.span(), "expected `repr` attribute"))?;
33    let option = enum_arg.unwrap_or_default();
34
35    let variants_data = variants
36        .iter()
37        .map(EnumReprVariant::from_variant)
38        .collect::<syn::Result<Vec<_>>>()?;
39
40    let mut default = None;
41    for v in &variants_data {
42        if v.is_default {
43            if default.is_some() {
44                return Err(syn::Error::new(
45                    v.ident.span(),
46                    "duplicate `default` attribute",
47                ));
48            }
49            default = Some(v.ident);
50        }
51    }
52
53    let mut final_variants = Vec::with_capacity(variants.len());
54    let mut prev_discriminant: Option<Expr> = None;
55
56    for variant in &variants_data {
57        let discriminant = match variant.discriminant {
58            Some(expr) => expr.clone(),
59            None => {
60                if !option.implicit {
61                    return Err(syn::Error::new(
62                        variant.ident.span(),
63                        "expected explicit discriminant (add #[enum_repr(implicit)] to enum attribute if you want it implicit)",
64                    ));
65                }
66                match prev_discriminant {
67                    Some(prev) => syn::parse_quote! { #prev + 1 },
68                    None => syn::parse_quote! { 0 },
69                }
70            }
71        };
72        prev_discriminant = Some(discriminant.clone());
73        final_variants.push(FinalVariantData {
74            ident: variant.ident,
75            discriminant,
76        });
77    }
78
79    internal::derive_enum_repr_internal(&input, option, default, final_variants, repr)
80}