1use core::ops;
4
5use proc_macro2::TokenStream;
6use quote::ToTokens;
7use syn::{
8 parse::{Parse, ParseStream},
9 Fields, Ident, ItemEnum, Result, Type,
10};
11
12pub struct EnumData {
14 repr: ItemEnum,
15 field_types: Vec<Type>,
16}
17
18impl EnumData {
19 pub fn field_types(&self) -> impl ExactSizeIterator<Item = &Type> + Clone {
30 self.field_types.iter()
31 }
32
33 pub fn variant_idents(&self) -> impl ExactSizeIterator<Item = &Ident> + Clone {
44 self.variants.iter().map(|v| &v.ident)
45 }
46}
47
48impl ops::Deref for EnumData {
49 type Target = ItemEnum;
50
51 fn deref(&self) -> &Self::Target {
52 &self.repr
53 }
54}
55
56impl From<EnumData> for ItemEnum {
57 fn from(other: EnumData) -> Self {
58 other.repr
59 }
60}
61
62impl Parse for EnumData {
63 fn parse(input: ParseStream<'_>) -> Result<Self> {
64 let item: ItemEnum = input.parse()?;
65
66 if item.variants.is_empty() {
67 bail!(item, "may not be used on enums without variants");
68 }
69
70 let field_types = item.variants.iter().try_fold(
71 Vec::with_capacity(item.variants.len()),
72 |mut field_types, v| {
73 if let Some((_, e)) = &v.discriminant {
74 bail!(e, "may not be used on enums with discriminants");
75 }
76
77 if v.fields.is_empty() {
78 bail!(v, "may not be used on enums with variants with zero fields");
79 } else if v.fields.len() != 1 {
80 bail!(v, "may not be used on enums with variants with multiple fields");
81 }
82
83 match &v.fields {
84 Fields::Unnamed(f) => {
85 field_types.push(f.unnamed.iter().next().unwrap().ty.clone());
86 Ok(field_types)
87 }
88 Fields::Named(_) => {
89 bail!(v, "may not be used on enums with variants with named fields");
90 }
91 Fields::Unit => unreachable!(),
92 }
93 },
94 )?;
95
96 Ok(Self { repr: item, field_types })
97 }
98}
99
100impl ToTokens for EnumData {
101 fn to_tokens(&self, tokens: &mut TokenStream) {
102 self.repr.to_tokens(tokens);
103 }
104}