1use heck::ToSnakeCase;
2use proc_macro::TokenStream;
3use proc_macro2::{Ident, Span};
4use quote::{quote, ToTokens};
5use syn::{
6 parse::Parse, parse_quote, punctuated::Punctuated, spanned::Spanned, Arm, Data, DataEnum,
7 DeriveInput, Expr, ExprMatch, Fields, FnArg, ImplItemFn, ItemFn, LitByteStr, Result, Token,
8 Type, Variant, Visibility,
9};
10
11pub fn parse(item: DeriveInput) -> TokenStream {
13 let name = &item.ident;
14 let name_str = name.to_string();
15 let name_bytes = LitByteStr::new(name_str.as_bytes(), Span::call_site());
16
17 if name_str.len() > 32 {
19 panic!("Event name too long: {name_str}");
20 }
21
22 let Data::Enum(event_enum) = &item.data else {
24 panic!("Event can only be derived for enums");
25 };
26
27 let mut expr_match: ExprMatch = parse_quote!(match self {});
29 let variant_fns = event_enum
30 .variants
31 .iter()
32 .map(|variant| impl_variant_fns(variant, &mut expr_match))
33 .collect::<Vec<_>>();
34
35 quote! {
37 impl #name {
38 pub const fn name() -> &'static [u8] {
40 #name_bytes
41 }
42
43 pub fn emit_name() {
45 unsafe { zink::ffi::evm::log0(Self::name()) }
46 }
47
48 #(#variant_fns)*
49
50 pub fn emit(self) {
52 #expr_match
53 }
54 }
55 }
56 .into()
57}
58
59fn impl_variant_fns(variant: &Variant, expr_match: &mut ExprMatch) -> ImplItemFn {
61 let name = &variant.ident;
62 let topic = variant.fields.len();
63
64 let mut inputs: Punctuated<FnArg, Token![,]> = Punctuated::new();
66 let mut args: Vec<Ident> = Vec::new();
67 for (index, field) in variant.fields.iter().enumerate() {
68 let var = field
69 .ident
70 .clone()
71 .unwrap_or(Ident::new(&format!("param_{index}"), Span::call_site()));
72 let ty = &field.ty;
73
74 args.push(var.clone());
75 inputs.push(FnArg::Typed(parse_quote!(#var: #ty)));
76 }
77
78 let name_snake: Ident = Ident::new(&name.to_string().to_snake_case(), Span::call_site());
80
81 let arm: Arm = parse_quote! {
83 Self::#name( #(#args),* ) => Self::#name_snake( #(#args),* ),
84 };
85 expr_match.arms.push(arm);
86
87 let logn = Ident::new(&format!("log{topic}"), Span::call_site());
89 let args = args
90 .iter()
91 .map(|arg| quote!(#arg.bytes32()))
92 .collect::<Vec<_>>();
93 parse_quote! {
94 pub fn #name_snake(#inputs) {
95 unsafe {zink::ffi::evm::#logn(#(#args),*, &Self::name()) }
96 }
97 }
98}