casper-node-macros
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
NameOfReactor
and - 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_a
andcomponent_b
respectively, - 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_2
to the first andconstructor_arg1
to the second component, CompA::new
will return just the component, whileCompB::new
will return a tuple of(component, effects)
, indicated by the@
prefix,- two variants
NameOfReactorEvent::ComponentA
andNameOfReactorEvent::ComponentB
will be added to the reactors event type, - these events will wrap
crate::components::comp_a::Event
(see caveat below) andcrate::components::comp_b::Event
respectively, - a
From<crate::components::comp_a::Event>
impl will be generated forNameOfReactorEvent
(similarly forcomp_b
), - the appropriate variants will similarly be added to the
NameOfReactorError
enum, - and all variants of
NameOfReactorEvent
that wrap a component event will be forwarded to that component'shandle_event
function.
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,
StorageRequest
s are routed tocomponent_a
,NetworkRequest
s are routed tocomponent_b
,ThirdRequest
s are routed tocomponent_a
(note that multiple requests can be routed to a single component instance), andAnotherRequest
is discarded entirely.
Routing a request ExampleRequest
to an example_target
means that
- a
NameOfReactorEvent::ExampleRequest(ExampleRequest)
variant is generated, - a
From<ExampleRequest>
is generated forNameOfReactorEvent
and - when dispatching a
NameOfReactorEvent::ExampleRequest
it 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.