#[map]Expand description
Marks a function as a Substreams map handler, generating the necessary WASM boilerplate.
§Panic Handling
Panics in handlers are trapped by the Substreams Engine and reported as deterministic
errors back to the user. This includes panics from Result::Err returns (which the
generated code converts to panics) and any explicit panic!() calls in your handler.
§Options
The macro accepts the following comma-separated options:
| Option | Description |
|---|---|
no_testable | Disables generation of the testable __impl_<name> function |
keep_empty_output | Prevents calling substreams::skip_empty_output() |
§Basic Usage
ⓘ
#[substreams::handlers::map]
fn map_transfers(blk: eth::Block) -> Result<pb::Transfers, Error> {
// handler logic
}§Generated Code (default)
By default, the macro generates two functions:
- A testable
__impl_<name>function with the original signature (always compiled) - A WASM export function that calls the testable function (only on
wasm32target)
For the example above, the macro generates:
ⓘ
// Testable function - always available, can be called directly in tests
pub fn __impl_map_transfers(blk: eth::Block) -> Result<pb::Transfers, Error> {
// user's handler body
}
// WASM export - only compiled for wasm32 target
#[cfg(target_arch = "wasm32")]
#[no_mangle]
pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
substreams::register_panic_hook();
let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
.unwrap_or_else(|_| panic!("Unable to decode..."));
substreams::skip_empty_output();
let result = __impl_map_transfers(blk);
if result.is_err() {
panic!("{:?}", result.unwrap_err())
}
substreams::output(result.expect("already checked"));
}§Testing Your Handlers
You can test your handler directly using the generated __impl_ function:
ⓘ
#[test]
fn test_map_transfers() {
let blk = eth::Block::default();
// Call the testable function directly
let result = __impl_map_transfers(blk);
assert!(result.is_ok());
// Or use the test_map! macro for convenience
let result = substreams::test_map!(map_transfers(blk));
assert!(result.is_ok());
}§Disabling Testable Generation
Use no_testable to generate only the WASM export (legacy behavior):
ⓘ
#[substreams::handlers::map(no_testable)]
fn map_transfers(blk: eth::Block) -> Result<pb::Transfers, Error> {
// handler logic
}This generates a single function without the __impl_ wrapper:
ⓘ
#[no_mangle]
pub extern "C" fn map_transfers(blk_ptr: *mut u8, blk_len: usize) {
substreams::register_panic_hook();
let func = || -> Result<pb::Transfers, Error> {
let blk: eth::Block = substreams::proto::decode_ptr(blk_ptr, blk_len)
.unwrap_or_else(|_| panic!("Unable to decode..."));
// user's handler body
};
substreams::skip_empty_output();
let result = func();
if result.is_err() {
panic!("{:?}", result.unwrap_err())
}
substreams::output(result.expect("already checked"));
}§Combining Options
Options can be combined with commas:
ⓘ
#[substreams::handlers::map(no_testable, keep_empty_output)]
fn map_transfers(blk: eth::Block) -> Result<pb::Transfers, Error> {
// handler logic
}§Supported Return Types
Result<T, Error>- Panics on error, outputsTon successResult<Option<T>, Error>- Panics on error, outputsTifSome, nothing ifNoneOption<T>- OutputsTifSome, nothing ifNoneT- OutputsTdirectly