more_convert_derive_internal/enum_repr/
mod.rs1use 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}