casper-node-macros
License
Licensed under the Apache License Version 2.0.
The casper-node-macros crate offers an easy-to-use macro to create reactor implementations for the component system. It enforces a set of convention and allows generating a large amount of otherwise boilerplate heavy setup code comfortably.
The macro is invoked by calling the cosy_macro::reactor macro as follows:
reactor!;
The sections in detail:
Outer definition
The definition of
reactor!;
indicates that
- the newly created reactor will be named
NameOfReactorand - its configuration type will be
ConfigurationType.
The types NameOfReactorEvent and NameOfReactorError will be automatically generated as well.
Component definition
Components are defined in the first section:
components:
Here
- two components will be defined as fields on the reactor struct, the fields being named
component_aandcomponent_brespectively, - their type will be
crate::components::comp_a::CompA(automatically deduced from the name), - they will be constructed passing in
constructor_arg_1,constructor_arg_2to the first andconstructor_arg1to the second component, CompA::newwill return just the component, whileCompB::newwill return a tuple of(component, effects), indicated by thehas_effectskeyword,- two variants
NameOfReactorEvent::ComponentAandNameOfReactorEvent::ComponentBwill be added to the reactors event type, - these events will wrap
crate::components::comp_a::Event(see caveat below) andcrate::components::comp_b::Eventrespectively, - a
From<crate::components::comp_a::Event>impl will be generated forNameOfReactorEvent(similarly forcomp_b), - the appropriate variants will similarly be added to the
NameOfReactorErrorenum, - and all variants of
NameOfReactorEventthat wrap a component event will be forwarded to that component'shandle_eventfunction.
Note that during construction, the parameters cfg, registry, event_queue and rng are available, as well as the local variable effect_builder.
Event overrides
Ideally all NameOfReactorEvent newtype variants would be written as NameOfReactorEvent::SomeComponent(<crate::components::some_component::SomeComponent as Component<Self>::Event> in the generated code, which unfortunately is not possible due to a current shortcoming in the Rust trait system that will likely only be fixed with chalk.
As a workaround, NameOfReactorEvent::SomeComponent(crate::components::some_component::Event will be used instead. This solution only works for event types that do not have their own type parameters. If they have, the Event portion can be replaced using the event override section of the macro invocation:
events:
This will result in crate::components::comp_a::Event<TypeArg> to be used to set the newtypes inner value instead.
Request routing
The third section defines how requests are routed:
requests:
In the example,
StorageRequests are routed tocomponent_a,NetworkRequests are routed tocomponent_b,ThirdRequests are routed tocomponent_a(note that multiple requests can be routed to a single component instance), andAnotherRequestis discarded quietly.
Instead of #, a request can be routed to !, which will panic once it receives a request.
Routing a request ExampleRequest to an example_target means that
- a
NameOfReactorEvent::ExampleRequest(ExampleRequest)variant is generated, - a
From<ExampleRequest>is generated forNameOfReactorEventand - when dispatching a
NameOfReactorEvent::ExampleRequestit will be turned intoexample_target's event type usingFrom, then dispatched toexample_target'shandle_event.
Not routing a request means that the reactor does not support components that require it.
Announcement routing
Announcements are routed almost exactly like requests
announcements:
with the key difference being that instead of a single target, an announcement is routed to zero or more instead. ! and # can be used as targets the same way they are used with requests as well.