1use proc_macro::TokenStream;
2use quote::{format_ident, quote, ToTokens};
3use syn::{parse_macro_input, Data, DeriveInput, Fields, Type};
4
5#[proc_macro_derive(IterAll)]
6pub fn iter_all_macro(input: TokenStream) -> TokenStream {
7 let input = parse_macro_input!(input as DeriveInput);
8
9 let enum_name = &input.ident;
10
11 let Data::Enum(ref data_enum) = input.data else {
12 panic!("IterAll can only be derived for enums");
13 };
14
15 let variants = data_enum.variants.iter().map(|variant| {
16 let variant_name = &variant.ident;
17
18 if let Fields::Unit = variant.fields {
19 return quote! {
20 action(#enum_name::#variant_name);
21 };
22 }
23
24 quote! {
25 action(#enum_name::#variant_name(Default::default()));
26 }
27 });
28
29 let variants = quote! {
30 #(#variants)*
31 };
32
33 let variants3 = data_enum.variants.iter().map(|variant| {
34 let variant_name = &variant.ident;
35
36 let uppercase_ident = format_ident!("{}", variant_name.to_string().to_uppercase());
37
38 if let Fields::Unit = variant.fields {
39 return quote! {
40 pub const #uppercase_ident: #enum_name = #enum_name::#variant;
41 };
42 }
43
44 let Fields::Unnamed(ref unnamed) = variant.fields else {
45 panic!();
46 };
47
48 let ty: &Type = &unnamed.unnamed.first().unwrap().ty;
49
50 let ty_str = ty.to_owned().to_token_stream().to_string();
51
52 match (ty_str.find('<'), ty_str.rfind('>')) {
53 (Some(start), Some(end)) => {
54 let generics = &ty_str[start + 1..end].trim();
55
56 let only_type = &ty_str[0..start - 1].trim();
57
58 let only_type = format_ident!("{}", only_type);
59
60 use std::str::FromStr;
61
62 let generics: proc_macro2::TokenStream =
63 TokenStream::from_str(generics).unwrap().to_owned().into();
64
65 quote! {
66 pub const #uppercase_ident: #ty = #only_type::<#generics>::new();
67 }
68 }
69 _ => quote! {
70 pub const #uppercase_ident: #ty = #ty::new();
71 },
72 }
73 });
74
75 let variants3 = quote! {
76 #(#variants3)*
77 };
78
79 quote! {
80 impl iter_all::IterAll for #enum_name {
81 fn iter_all(mut action: impl FnMut(Self)) {
82 #variants
83 }
84 }
85
86 impl #enum_name {
87 #variants3
88 }
89 }
90 .into()
91}