ghost_crab_macros/
lib.rs

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    // Checks that the metadata does not have more than 3 comma separated values
77    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}