1use proc_macro::TokenStream;
2
3mod assertions;
4mod config;
5mod errors;
6mod handler;
7mod store;
8
9#[proc_macro_attribute]
132pub fn map(args: TokenStream, item: TokenStream) -> TokenStream {
133 let options = config::HandlerOptions::parse(&args.to_string())
134 .unwrap_or_else(|e| panic!("Invalid arguments for map macro: {}", e));
135 handler::main(item.into(), config::ModuleType::Map, options).into()
136}
137
138#[proc_macro_attribute]
169pub fn store(args: TokenStream, item: TokenStream) -> TokenStream {
170 let options = config::HandlerOptions::parse(&args.to_string())
171 .unwrap_or_else(|e| panic!("Invalid arguments for store macro: {}", e));
172 handler::main(item.into(), config::ModuleType::Store, options).into()
173}
174
175#[proc_macro_derive(StoreWriter)]
177pub fn derive(input: TokenStream) -> TokenStream {
178 store::main(input)
179}
180
181#[proc_macro]
207pub fn test_map(input: TokenStream) -> TokenStream {
208 use proc_macro2::TokenStream as TokenStream2;
209 use quote::quote;
210 use syn::{parse_macro_input, Expr, ExprCall, ExprPath};
211
212 let call = parse_macro_input!(input as ExprCall);
213
214 let func_expr = &*call.func;
216 let args = &call.args;
217
218 match func_expr {
219 Expr::Path(ExprPath { path, .. }) => {
220 let mut new_path = path.clone();
222 if let Some(last_segment) = new_path.segments.last_mut() {
223 let new_name = quote::format_ident!("__impl_{}", last_segment.ident);
224 last_segment.ident = new_name;
225 }
226
227 let result: TokenStream2 = quote! {
228 #new_path(#args)
229 };
230 result.into()
231 }
232 _ => {
233 panic!("test_map! expects a function call expression, e.g., test_map!(handler(args))")
234 }
235 }
236}
237
238#[cfg(test)]
239mod test {
240 use crate::{
241 assertions::assert_ast_eq,
242 config::{HandlerOptions, ModuleType},
243 handler::main,
244 };
245 use quote::quote;
246
247 fn opts(keep_empty_output: bool, no_testable: bool) -> HandlerOptions {
248 HandlerOptions {
249 keep_empty_output,
250 no_testable,
251 }
252 }
253
254 #[test]
256 fn test_map_default_plain() {
257 let item = quote! {
258 fn map_transfers(blk: eth::Block) -> pb::Custom {
259 unimplemented!("do something");
260 }
261 };
262
263 assert_ast_eq(
264 main(item, ModuleType::Map, opts(true, false)).into(),
265 quote! {
266 #[no_mangle]
267 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
268 substreams::register_panic_hook();
269 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
270 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
271 let result = __impl_map_transfers(blk);
272 substreams::output(result);
273 }
274
275 pub fn __impl_map_transfers(blk: eth::Block) -> pb::Custom {
276 unimplemented!("do something");
277 }
278 },
279 );
280 }
281
282 #[test]
283 fn test_map_default_mut() {
284 let item = quote! {
285 fn map_transfers(mut blk: eth::Block) -> pb::Custom {
286 unimplemented!("do something");
287 }
288 };
289
290 assert_ast_eq(
291 main(item, ModuleType::Map, opts(true, false)).into(),
292 quote! {
293 #[no_mangle]
294 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
295 substreams::register_panic_hook();
296 let mut blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
297 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
298 let result = __impl_map_transfers(blk);
299 substreams::output(result);
300 }
301
302 pub fn __impl_map_transfers(mut blk: eth::Block) -> pb::Custom {
303 unimplemented!("do something");
304 }
305 },
306 );
307 }
308
309 #[test]
310 fn test_map_default_result() {
311 let item = quote! {
312 fn map_transfers(blk: eth::Block) -> Result<pb::Custom, Error> {
313 unimplemented!("do something");
314 }
315 };
316
317 assert_ast_eq(
318 main(item, ModuleType::Map, opts(true, false)).into(),
319 quote! {
320 #[no_mangle]
321 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
322 substreams::register_panic_hook();
323 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
324 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
325 let result = __impl_map_transfers(blk);
326 if result.is_err() {
327 panic!("{:?}", result.unwrap_err())
328 }
329 substreams::output(result.expect("already checked that result is not an error"));
330 }
331
332 pub fn __impl_map_transfers(blk: eth::Block) -> Result<pb::Custom, Error> {
333 unimplemented!("do something");
334 }
335 },
336 );
337 }
338
339 #[test]
340 fn test_map_default_with_string_param() {
341 let item = quote! {
342 fn map_transfers(params: String, blk: eth::Block) -> Result<pb::Custom, Error> {
343 unimplemented!("do something");
344 }
345 };
346
347 assert_ast_eq(
348 main(item, ModuleType::Map, opts(true, false)).into(),
349 quote! {
350 #[no_mangle]
351 pub extern "C" fn map_transfers(params_ptr: *mut u8, params_len: usize, blk_ptr: *mut u8, blk_len: usize) {
352 substreams::register_panic_hook();
353 let params: String = std::mem::ManuallyDrop::new(unsafe { String::from_raw_parts(params_ptr, params_len, params_len) }).to_string();
354 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
355 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
356 let result = __impl_map_transfers(params, blk);
357 if result.is_err() {
358 panic!("{:?}", result.unwrap_err())
359 }
360 substreams::output(result.expect("already checked that result is not an error"));
361 }
362
363 pub fn __impl_map_transfers(params: String, blk: eth::Block) -> Result<pb::Custom, Error> {
364 unimplemented!("do something");
365 }
366 },
367 );
368 }
369
370 #[test]
371 fn test_map_default_with_readable_store() {
372 let item = quote! {
373 fn map_transfers(blk: eth::Block, store: StoreGetProto<pb::Pairs>) -> Result<pb::Custom, Error> {
374 unimplemented!("do something");
375 }
376 };
377
378 assert_ast_eq(
379 main(item, ModuleType::Map, opts(true, false)).into(),
380 quote! {
381 #[no_mangle]
382 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize, store_idx: u32) {
383 substreams::register_panic_hook();
384 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
385 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
386 let store: StoreGetProto<pb::Pairs> = StoreGetProto::new(store_idx);
387 let result = __impl_map_transfers(blk, store);
388 if result.is_err() {
389 panic!("{:?}", result.unwrap_err())
390 }
391 substreams::output(result.expect("already checked that result is not an error"));
392 }
393
394 pub fn __impl_map_transfers(blk: eth::Block, store: StoreGetProto<pb::Pairs>) -> Result<pb::Custom, Error> {
395 unimplemented!("do something");
396 }
397 },
398 );
399 }
400
401 #[test]
402 fn test_map_default_with_writable_store() {
403 let item = quote! {
404 fn map_transfers(blk: eth::Block, output: StoreAddInt64) -> Result<pb::Custom, Error> {
405 unimplemented!("do something");
406 }
407 };
408
409 assert_ast_eq(
410 main(item, ModuleType::Map, opts(true, false)).into(),
411 quote! {
412 #[no_mangle]
413 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
414 substreams::register_panic_hook();
415 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
416 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
417 let output: StoreAddInt64 = StoreAddInt64::new();
418 let result = __impl_map_transfers(blk, output);
419 if result.is_err() {
420 panic!("{:?}", result.unwrap_err())
421 }
422 substreams::output(result.expect("already checked that result is not an error"));
423 }
424
425 pub fn __impl_map_transfers(blk: eth::Block, output: StoreAddInt64) -> Result<pb::Custom, Error> {
426 unimplemented!("do something");
427 }
428 },
429 );
430 }
431
432 #[test]
434 fn test_map_no_testable_plain() {
435 let item = quote! {
436 fn map_transfers(blk: eth::Block) -> pb::Custom {
437 unimplemented!("do something");
438 }
439 };
440
441 assert_ast_eq(
442 main(item, ModuleType::Map, opts(true, true)).into(),
443 quote! {
444 #[no_mangle]
445 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
446 substreams::register_panic_hook();
447 let func = || -> pb::Custom {
448 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
449 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
450 let result = {
451 unimplemented!("do something");
452 };
453 result
454 };
455 let result = func();
456 substreams::output(result);
457 }
458 },
459 );
460 }
461
462 #[test]
463 fn test_map_no_testable_option() {
464 let item = quote! {
465 fn map_transfers(blk: eth::Block) -> Option<pb::Custom> {
466 unimplemented!("do something");
467 }
468 };
469
470 assert_ast_eq(
471 main(item, ModuleType::Map, opts(true, true)).into(),
472 quote! {
473 #[no_mangle]
474 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
475 substreams::register_panic_hook();
476 let func = || -> Option<pb::Custom> {
477 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
478 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
479 let result = { unimplemented!("do something"); };
480 result
481 };
482
483 let result = func();
484 if let Some(value) = result {
485 substreams::output(value);
486 }
487 }
488 },
489 );
490 }
491
492 #[test]
493 fn test_map_no_testable_result_option() {
494 let item = quote! {
495 fn map_transfers(blk: eth::Block) -> Result<Option<pb::Custom>> {
496 unimplemented!("do something");
497 }
498 };
499
500 assert_ast_eq(
501 main(item.clone(), ModuleType::Map, opts(true, true)).into(),
502 quote! {
503 #[no_mangle]
504 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
505 substreams::register_panic_hook();
506 let func = || -> Result<Option<pb::Custom> > {
507 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
508 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
509 let result = { unimplemented!("do something"); };
510 result
511 };
512
513 let result = func();
514 if result.is_err() {
515 panic!("{:?}", result.unwrap_err())
516 }
517 if let Some(inner) = result.expect("already checked that result is not an error") {
518 substreams::output(inner);
519 }
520 }
521 },
522 );
523
524 assert_ast_eq(
525 main(item, ModuleType::Map, opts(false, true)).into(),
526 quote! {
527 #[no_mangle]
528 pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
529 substreams::register_panic_hook();
530 let func = || -> Result<Option<pb::Custom> > {
531 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
532 .unwrap_or_else(|_| panic!("Unable to decode Protobuf data ({} bytes) to '{}' message's struct", blk_len, stringify!(eth::Block)));
533 let result = { unimplemented!("do something"); };
534 result
535 };
536
537 substreams :: skip_empty_output () ;
538 let result = func();
539 if result.is_err() {
540 panic!("{:?}", result.unwrap_err())
541 }
542 if let Some(inner) = result.expect("already checked that result is not an error") {
543 substreams::output(inner);
544 }
545 }
546 },
547 );
548 }
549
550 #[test]
551 fn test_store_handler() {
552 let item = quote! {
553 fn store_values(blk: eth::Block, store: StoreAddInt64) {
554 unimplemented!("do something");
555 }
556 };
557
558 assert_ast_eq(
559 main(item.clone(), ModuleType::Store, opts(true, false)).into(),
560 quote! {
561 #[no_mangle]
562 pub extern "C" fn store_values(blk_ptr: *mut u8, blk_len: usize) {
563 substreams::register_panic_hook();
564 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
565 .unwrap_or_else(|_|
566 panic!(
567 "Unable to decode Protobuf data ({} bytes) to '{}' message's struct",
568 blk_len, stringify!(eth::Block)
569 )
570 );
571 let store: StoreAddInt64 = StoreAddInt64::new();
572 let result = {
573 unimplemented!("do something");
574 };
575 result
576 }
577 },
578 );
579
580 assert_ast_eq(
581 main(item, ModuleType::Store, opts(false, false)).into(),
582 quote! {
583 #[no_mangle]
584 pub extern "C" fn store_values(blk_ptr: *mut u8, blk_len: usize) {
585 substreams::register_panic_hook();
586 let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
587 .unwrap_or_else(|_|
588 panic!(
589 "Unable to decode Protobuf data ({} bytes) to '{}' message's struct",
590 blk_len, stringify!(eth::Block)
591 )
592 );
593 let store: StoreAddInt64 = StoreAddInt64::new();
594 substreams :: skip_empty_output () ;
595 let result = {
596 unimplemented!("do something");
597 };
598 result
599 }
600 },
601 );
602 }
603}