#[behavior]Expand description
Generate the nominal protocol, closed effect products, and exact Behavior
wiring for an inherent impl. addr and message are required. Omitting
sends, births, or error selects the capability-free NoSends,
NoBirths, or Never type respectively.
A sends = { lane: Product } declaration generates ActorSends, one
distinct ActorSendsLane selector per field, and structural SendEffects,
SendsFor, and InterpretSends implementations. It also generates an
ActorActions extension trait with one fluent send_lane method per named
lane. Each method delegates to AppendSend, changing only the send leg
while preserving creations and the exact next-behavior verdict. A
births = { lane: Child } declaration generates ActorChildren as the
exact recursive ChildChoice produced by Children calls in declaration
order. It also generates ActorChildrenRoutes, containing one nominally
distinct ChildRoute per declared role. A route is the single typed
source for staging that role’s creation and addressing its creator-local
recipient. Creation remains an authored Children value and is never
performed by the macro.
Invalid receivers are rejected at compile time.
use behavior::{Actions, BehaviorActed, MailAddr};
struct Invalid;
#[behavior::behavior(
addr = MailAddr,
message = u8,
)]
impl Invalid {
fn init(&self) -> BehaviorActed<Self> {
Ok(Actions::cont())
}
fn receive(&mut self, _: MailAddr, _: u8) -> BehaviorActed<Self> {
Ok(Actions::cont())
}
}Missing receive methods are rejected by the macro itself:
use behavior::MailAddr;
struct Missing;
#[behavior::behavior(
addr = MailAddr,
message = u8,
)]
impl Missing {
}Async behavior methods cannot introduce an erased or alternate execution path:
use behavior::{Actions, BehaviorActed, MailAddr};
struct Async;
#[behavior::behavior(
addr = MailAddr,
message = u8,
)]
impl Async {
async fn init(&mut self) -> BehaviorActed<Self> {
Ok(Actions::cont())
}
fn receive(&mut self, _: MailAddr, _: u8) -> BehaviorActed<Self> {
Ok(Actions::cont())
}
}Undeclared send lanes have no selector and cannot be emitted:
use behavior::{Actions, BehaviorActed, MailAddr, SendEffects};
struct Sender;
#[behavior::behavior(addr = MailAddr, message = (), sends = { replies: Vec<u8> })]
impl Sender {
fn receive(&mut self, _: MailAddr, _: ()) -> BehaviorActed<Self> {
let mut sends = SenderSends::empty();
sends.send::<_, SenderSendsUndeclared>(1);
Ok(Actions::send(sends))
}
}Generated lane methods accept only inputs supported by that lane:
use behavior::{Actions, BehaviorActed, MailAddr};
struct Sender;
#[behavior::behavior(addr = MailAddr, message = (), sends = { replies: Vec<u8> })]
impl Sender {
fn receive(&mut self, _: MailAddr, _: ()) -> BehaviorActed<Self> {
Ok(Actions::cont().send_replies("not a u8"))
}
}A child absent from the declared closed birth product cannot be created:
use behavior::{Actions, BehaviorActed, Create, MailAddr};
struct Declared;
struct Other;
struct Root;
#[behavior::behavior(addr = MailAddr, message = (), births = { declared: Declared })]
impl Root {
fn receive(&mut self, _: MailAddr, _: ()) -> BehaviorActed<Self> {
Ok(Actions::create(vec![Create::birth(1, Other)]))
}
}Every generated send lane remains a separate interpreter obligation:
use behavior::{BehaviorActed, Delivery, InterpretDelivery, InterpretSends, MailAddr,
MessageProtocol, Recipient, SendInterpreter};
struct Root;
type First = MessageProtocol<MailAddr, u8>;
type Second = MessageProtocol<MailAddr, u16>;
#[behavior::behavior(addr = MailAddr, message = (), sends = {
first: Vec<Delivery<First>>,
second: Vec<Delivery<Second>>,
})]
impl Root {
fn receive(&mut self, _: MailAddr, _: ()) -> BehaviorActed<Self> {
Ok(behavior::Actions::cont())
}
}
struct Incomplete;
impl SendInterpreter for Incomplete { type Error = (); }
impl InterpretDelivery<First> for Incomplete {
fn interpret_delivery(&mut self, _: Delivery<First>) -> impl core::future::Future<Output = Result<(), ()>> + Send {
async { Ok(()) }
}
}
fn require_complete()
where
RootSends: InterpretSends<Incomplete, behavior::User<MailAddr, ()>, behavior::Here>,
{}Generated child products likewise require an installer for every declared alternative:
use behavior::{BehaviorActed, Create, DispatchBirth, InstallBirth, MailAddr, Never};
struct First;
struct Second;
#[behavior::behavior(addr = MailAddr, message = Never)]
impl First {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> { match message {} }
}
#[behavior::behavior(addr = MailAddr, message = Never)]
impl Second {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> { match message {} }
}
struct Root;
#[behavior::behavior(addr = MailAddr, message = (), births = {
first: First,
second: Second,
})]
impl Root {
fn receive(&mut self, _: MailAddr, _: ()) -> BehaviorActed<Self> {
Ok(behavior::Actions::cont())
}
}
struct Incomplete;
impl InstallBirth<MailAddr, First, (), Never> for Incomplete {
async fn install_birth(&mut self, _: Create<MailAddr, First>) -> Result<(), Never> { Ok(()) }
}
fn require_complete<T: DispatchBirth<MailAddr, Incomplete, (), Never>>() {}
require_complete::<RootChildren>();A generated child route accepts only its declared behavior:
use behavior::{BehaviorActed, Children, MailAddr, Never};
struct Declared;
#[behavior::behavior(addr = MailAddr, message = Never)]
impl Declared {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
struct Other;
#[behavior::behavior(addr = MailAddr, message = Never)]
impl Other {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
struct Root;
#[behavior::behavior(addr = MailAddr, message = Never, births = { worker: Declared })]
impl Root {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
let routes = RootChildrenRoutes::new(1);
let _ = Children::<MailAddr>::new().child_at(routes.worker, Other);Two declared roles remain distinct even when they use the same behavior:
use behavior::{BehaviorActed, ChildRoute, MailAddr, Never};
struct Worker;
#[behavior::behavior(addr = MailAddr, message = Never)]
impl Worker {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
struct Root;
#[behavior::behavior(addr = MailAddr, message = Never, births = {
primary: Worker,
backup: Worker,
})]
impl Root {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
fn requires_primary(_: ChildRoute<Worker, RootChildrenPrimary>) {}
let routes = RootChildrenRoutes::new(1, 2);
requires_primary(routes.backup);Named topology selectors accept only the child declared for that parent role, which lets an application builder remain entirely static:
use behavior::{Behavior, BehaviorActed, ChildRole, MailAddr, Never};
struct Worker;
#[behavior::behavior(addr = MailAddr, message = Never)]
impl Worker {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
struct Query;
#[behavior::behavior(addr = MailAddr, message = Never)]
impl Query {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
struct Root;
#[behavior::behavior(addr = MailAddr, message = Never, births = { workers: Worker })]
impl Root {
fn receive(&mut self, _: MailAddr, message: Never) -> BehaviorActed<Self> {
match message {}
}
}
fn child<Parent, Role>(_: Role, _: Role::Child)
where
Parent: Behavior,
Role: ChildRole<Parent>,
{}
child::<Root, _>(RootChild::Workers, Query);Generate the mechanical Behavior implementation for a normal inherent
impl containing receive(&mut self, from, message) and, optionally,
init(&mut self). Omitting init selects the behavior algebra’s empty
initialization transition. The original impl and methods are preserved
unchanged.