1extern crate proc_macro;
2use ghost_crab_common::config;
3use proc_macro::TokenStream;
4use proc_macro2::{Ident, Literal};
5use quote::{format_ident, quote};
6use syn::{parse_macro_input, ItemFn};
7
8#[proc_macro_attribute]
9pub fn event_handler(metadata: TokenStream, input: TokenStream) -> TokenStream {
10 create_handler(metadata, input, false)
11}
12
13#[proc_macro_attribute]
14pub fn template(metadata: TokenStream, input: TokenStream) -> TokenStream {
15 create_handler(metadata, input, true)
16}
17
18#[proc_macro_attribute]
19pub fn block_handler(metadata: TokenStream, input: TokenStream) -> TokenStream {
20 let name = metadata.to_string();
21 let name = name.trim();
22
23 if name.is_empty() {
24 panic!("The source is missing");
25 }
26
27 let config = config::load().expect("config.json not found");
28 let _ = config.block_handlers.get(name).expect("BlockHandler not found in the config.json");
29 let name = Literal::string(name);
30
31 let parsed = parse_macro_input!(input as ItemFn);
32 let fn_name = parsed.sig.ident.clone();
33 let fn_body = parsed.block;
34 let fn_args = parsed.sig.inputs.clone();
35
36 TokenStream::from(quote! {
37 pub struct #fn_name;
38
39 impl #fn_name {
40 pub fn new() -> Arc<Box<(dyn BlockHandler + Send + Sync)>> {
41 Arc::new(Box::new(#fn_name {}))
42 }
43 }
44
45 #[async_trait]
46 impl BlockHandler for #fn_name {
47 async fn handle(&self, #fn_args) {
48 #fn_body
49 }
50
51 fn name(&self) -> String {
52 String::from(#name)
53 }
54 }
55 })
56}
57
58fn get_source_and_event(metadata: TokenStream) -> (String, Ident) {
59 let metadata_string = metadata.to_string();
60 let mut metadata_split = metadata_string.split('.');
61
62 let name = metadata_split.next().expect("The source is missing");
63 let name = String::from(name.trim());
64
65 if name.is_empty() {
66 panic!("The source is empty");
67 }
68
69 let event_name = metadata_split.next().expect("The event name is missing");
70 let event_name = String::from(event_name.trim());
71
72 if event_name.is_empty() {
73 panic!("The event name is empty");
74 }
75
76 let should_be_none = metadata_split.next();
78 if should_be_none.is_some() {
79 panic!("The metadata has too many values");
80 }
81
82 let event_name = syn::Ident::new(&event_name, proc_macro2::Span::call_site());
83 return (name, event_name);
84}
85
86fn get_context_identifier(parsed: ItemFn) -> Ident {
87 let first_input = parsed.sig.inputs[0].clone();
88
89 let ctx = if let syn::FnArg::Typed(pat_type) = first_input {
90 if let syn::Pat::Ident(pat_ident) = *pat_type.pat {
91 pat_ident.ident
92 } else {
93 panic!("Malformed handler function arguments")
94 }
95 } else {
96 panic!("Malformed handler function arguments")
97 };
98
99 return ctx;
100}
101
102fn create_handler(metadata: TokenStream, input: TokenStream, is_template: bool) -> TokenStream {
103 let (name, event_name) = get_source_and_event(metadata);
104 let config = config::load().expect("config.json not found");
105
106 let abi = if is_template {
107 let source = config.templates.get(&name).expect("Source not found.");
108 Literal::string(&source.abi)
109 } else {
110 let source = config.data_sources.get(&name).expect("Source not found.");
111 Literal::string(&source.abi)
112 };
113
114 let parsed = parse_macro_input!(input as ItemFn);
115 let fn_name = parsed.sig.ident.clone();
116 let fn_args = parsed.sig.inputs.clone();
117 let fn_body = parsed.block.clone();
118 let ctx = get_context_identifier(parsed);
119
120 let contract_name = format_ident!("{}Contract", fn_name);
121 let data_source = Literal::string(&name);
122
123 TokenStream::from(quote! {
124 sol!(
125 #[sol(rpc)]
126 #contract_name,
127 #abi
128 );
129
130 pub struct #fn_name;
131
132 impl #fn_name {
133 pub fn new() -> Arc<Box<(dyn EventHandler + Send + Sync)>> {
134 Arc::new(Box::new(#fn_name {}))
135 }
136 }
137
138 #[async_trait]
139 impl EventHandler for #fn_name {
140 async fn handle(&self, #fn_args) {
141 let decoded_log = #ctx
142 .log
143 .log_decode::<#contract_name::#event_name>()
144 .expect("Error decoding log data");
145
146 let event = decoded_log.data();
147
148 #fn_body
149 }
150
151 fn name(&self) -> String {
152 String::from(#data_source)
153 }
154
155 fn event_signature(&self) -> String {
156 #contract_name::#event_name::SIGNATURE.to_string()
157 }
158 }
159 })
160}