substreams_macro/
lib.rs

1use proc_macro::TokenStream;
2
3mod assertions;
4mod config;
5mod errors;
6mod handler;
7mod store;
8
9#[proc_macro_attribute]
10pub fn map(args: TokenStream, item: TokenStream) -> TokenStream {
11    let mut keep_empty_output = false;
12    match args.to_string().as_str() {
13        "" => {}
14        "keep_empty_output" => keep_empty_output = true,
15        _ => panic!("Invalid argument '{}' for map macro", args),
16    }
17    handler::main(item.into(), config::ModuleType::Map, keep_empty_output).into()
18}
19
20#[proc_macro_attribute]
21pub fn store(args: TokenStream, item: TokenStream) -> TokenStream {
22    let mut keep_empty_output = false;
23    match args.to_string().as_str() {
24        "" => {}
25        "keep_empty_output" => keep_empty_output = true,
26        _ => panic!("Invalid argument '{}' for store macro", args),
27    }
28    handler::main(item.into(), config::ModuleType::Store, keep_empty_output).into()
29}
30
31// todo: remove this once satisfied with implementation of StoreDelete
32#[proc_macro_derive(StoreWriter)]
33pub fn derive(input: TokenStream) -> TokenStream {
34    store::main(input)
35}
36
37#[cfg(test)]
38mod test {
39    use crate::{assertions::assert_ast_eq, config::ModuleType, handler::main};
40    use quote::quote;
41
42    #[test]
43    fn test_map_plain() {
44        let item = quote! {
45            fn map_transfers(blk: eth::Block) -> pb::Custom {
46                unimplemented!("do something");
47            }
48        };
49
50        assert_ast_eq(
51            main(item, ModuleType::Map, true).into(),
52            quote! {
53                #[no_mangle]
54                pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
55                    substreams::register_panic_hook();
56                    let func = || -> pb::Custom {
57                        let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
58                            .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
59                        let result = {
60                            unimplemented!("do something");
61                        };
62                        result
63                    };
64                    let result = func();
65                    substreams::output(result);
66                }
67            },
68        );
69    }
70
71    #[test]
72    fn test_map_mut() {
73        let item = quote! {
74            fn map_transfers(mut blk: eth::Block) -> pb::Custom {
75                unimplemented!("do something");
76            }
77        };
78
79        assert_ast_eq(
80            main(item, ModuleType::Map, true).into(),
81            quote! {
82                #[no_mangle]
83                pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
84                    substreams::register_panic_hook();
85                    let func = || -> pb::Custom {
86                        let mut blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
87                            .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
88                        let result = {
89                            unimplemented!("do something");
90                        };
91                        result
92                    };
93                    let result = func();
94                    substreams::output(result);
95                }
96            },
97        );
98    }
99
100    #[test]
101    fn test_map_option() {
102        let item = quote! {
103            fn map_transfers(blk: eth::Block) -> Option<pb::Custom> {
104                unimplemented!("do something");
105            }
106        };
107
108        assert_ast_eq(
109            main(item, ModuleType::Map, true).into(),
110            quote! {
111                #[no_mangle]
112                pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
113                    substreams::register_panic_hook();
114                    let func = || -> Option<pb::Custom> {
115                        let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
116                            .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
117                        let result = { unimplemented!("do something"); };
118                        result
119                    };
120
121                    let result = func();
122                    if let Some(value) = result {
123                        substreams::output(value);
124                    }
125                }
126            },
127        );
128    }
129
130    #[test]
131    fn test_map_result() {
132        let item = quote! {
133            fn map_transfers(blk: eth::Block) -> Result<pb::Custom> {
134                unimplemented!("do something");
135            }
136        };
137
138        assert_ast_eq(
139            main(item, ModuleType::Map, true).into(),
140            quote! {
141                #[no_mangle]
142                pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
143                    substreams::register_panic_hook();
144                    let func = || -> Result<pb::Custom> {
145                        let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
146                            .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
147                        let result = { unimplemented!("do something"); };
148                        result
149                    };
150
151                    let result = func();
152                    if result.is_err() {
153                        panic!("{:?}", result.unwrap_err())
154                    }
155                    substreams::output(result.expect("already checked that result is not an error"));
156                }
157            },
158        );
159    }
160
161    #[test]
162    fn test_map_result_option() {
163        let item = quote! {
164            fn map_transfers(blk: eth::Block) -> Result<Option<pb::Custom>> {
165                unimplemented!("do something");
166            }
167        };
168
169        assert_ast_eq(
170            main(item.clone(), ModuleType::Map, true).into(),
171            quote! {
172                #[no_mangle]
173                pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
174                    substreams::register_panic_hook();
175                    let func = || -> Result<Option<pb::Custom> > {
176                        let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
177                            .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
178                        let result = { unimplemented!("do something"); };
179                        result
180                    };
181
182                    let result = func();
183                    if result.is_err() {
184                        panic!("{:?}", result.unwrap_err())
185                    }
186                    if let Some(inner) = result.expect("already checked that result is not an error") {
187                        substreams::output(inner);
188                    }
189                }
190            },
191        );
192
193        assert_ast_eq(
194            main(item, ModuleType::Map, false).into(),
195            quote! {
196                #[no_mangle]
197                pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
198                    substreams::register_panic_hook();
199                    let func = || -> Result<Option<pb::Custom> > {
200                        let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
201                            .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
202                        let result = { unimplemented!("do something"); };
203                        result
204                    };
205
206                    substreams :: skip_empty_output () ;
207                    let result = func();
208                    if result.is_err() {
209                        panic!("{:?}", result.unwrap_err())
210                    }
211                    if let Some(inner) = result.expect("already checked that result is not an error") {
212                        substreams::output(inner);
213                    }
214                }
215            },
216        );
217    }
218
219    #[test]
220    fn test_store_result_option() {
221        let item = quote! {
222            fn store_values(blk: eth::Block, store: StoreAddInt64) {
223                unimplemented!("do something");
224            }
225        };
226
227        assert_ast_eq(
228            main(item.clone(), ModuleType::Store, true).into(),
229            quote! {
230                #[no_mangle]
231                pub extern "C" fn store_values(blk_ptr: *mut u8, blk_len: usize) {
232                    substreams::register_panic_hook();
233                    let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
234                        .unwrap_or_else(|_|
235                            panic!(
236                                "Unable to decode Protobuf data ({} bytes) to '{}' message's struct",
237                                blk_len, stringify!(eth::Block)
238                            )
239                        );
240                    let store: StoreAddInt64 = StoreAddInt64::new();
241                    let result = {
242                        unimplemented!("do something");
243                    };
244                    result
245                }
246            },
247        );
248
249        assert_ast_eq(
250            main(item, ModuleType::Store, false).into(),
251            quote! {
252                #[no_mangle]
253                    pub extern "C" fn store_values(blk_ptr: *mut u8, blk_len: usize) {
254                    substreams::register_panic_hook();
255                    let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
256                        .unwrap_or_else(|_|
257                            panic!(
258                                "Unable to decode Protobuf data ({} bytes) to '{}' message's struct",
259                                blk_len, stringify!(eth::Block)
260                            )
261                        );
262                    let store: StoreAddInt64 = StoreAddInt64::new();
263                    substreams :: skip_empty_output () ;
264                    let result = {
265                        unimplemented!("do something");
266                    };
267                    result
268                }
269            },
270        );
271    }
272}