behavior/actor/addressing.rs
1//! Protocol-indexed actor recipients and deliveries.
2
3use core::marker::PhantomData;
4
5use crate::Behavior;
6
7/// A pure actor-address namespace.
8pub trait Address: Copy + Eq {
9 type Nonce: Copy + Eq;
10
11 #[must_use]
12 fn birth(self, nonce: Self::Nonce) -> Self;
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct MailAddr(pub u64);
17
18impl From<u64> for MailAddr {
19 fn from(value: u64) -> Self {
20 Self(value)
21 }
22}
23
24impl From<MailAddr> for u64 {
25 fn from(value: MailAddr) -> Self {
26 value.0
27 }
28}
29
30impl Address for MailAddr {
31 type Nonce = u64;
32
33 fn birth(self, nonce: u64) -> Self {
34 Self(self.0 ^ nonce.wrapping_mul(0x9E37_79B9_7F4A_7C15))
35 }
36}
37
38/// Internal representation of pure routing intent.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub(crate) enum Route<A: Address> {
41 Global(A),
42 Child(A::Nonce),
43}
44
45impl<A: Address> Route<A> {
46 #[must_use]
47 pub(crate) const fn global(address: A) -> Self {
48 Self::Global(address)
49 }
50
51 #[must_use]
52 pub(crate) const fn child(nonce: A::Nonce) -> Self {
53 Self::Child(nonce)
54 }
55}
56
57/// Pure routing intent for one concrete destination behavior protocol.
58///
59/// The destination behavior is part of the type even when two protocols share
60/// the same address namespace and message type. The value contains no mailbox,
61/// endpoint, registry entry, or other interpreter-owned capability.
62pub struct Recipient<B: Behavior> {
63 route: Route<B::Addr>,
64 protocol: PhantomData<fn() -> B>,
65}
66
67impl<B: Behavior> Copy for Recipient<B> {}
68
69impl<B: Behavior> Clone for Recipient<B> {
70 fn clone(&self) -> Self {
71 *self
72 }
73}
74
75impl<B: Behavior> Recipient<B> {
76 #[must_use]
77 pub fn global(address: B::Addr) -> Self {
78 Self::from_route(Route::global(address))
79 }
80
81 #[must_use]
82 pub fn child(nonce: <B::Addr as Address>::Nonce) -> Self {
83 Self::from_route(Route::child(nonce))
84 }
85
86 /// Resolve this intent in the address namespace of the sending actor.
87 ///
88 /// Global recipients ignore `parent`; child recipients derive their
89 /// address from it. The route representation remains private so runtimes
90 /// cannot couple endpoint tables to Behaviorpass internals.
91 #[must_use]
92 pub fn resolve(self, parent: B::Addr) -> B::Addr {
93 match self.route {
94 Route::Global(address) => address,
95 Route::Child(nonce) => parent.birth(nonce),
96 }
97 }
98
99 #[doc(hidden)]
100 pub fn is_child(self, expected: <B::Addr as Address>::Nonce) -> bool {
101 matches!(self.route, Route::Child(nonce) if nonce == expected)
102 }
103
104 const fn from_route(route: Route<B::Addr>) -> Self {
105 Self {
106 route,
107 protocol: PhantomData,
108 }
109 }
110}
111
112impl<B: Behavior> PartialEq for Recipient<B> {
113 fn eq(&self, other: &Self) -> bool {
114 self.route == other.route
115 }
116}
117
118impl<B: Behavior> Eq for Recipient<B> {}
119
120impl<B: Behavior> core::fmt::Debug for Recipient<B>
121where
122 B::Addr: core::fmt::Debug,
123 <B::Addr as Address>::Nonce: core::fmt::Debug,
124{
125 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
126 self.route.fmt(f)
127 }
128}
129
130/// One pure communication addressed to a concrete behavior protocol.
131///
132/// Protocol identity is not inferred from the payload. Consequently, two
133/// behaviors accepting the same address and message types still have distinct
134/// delivery types.
135///
136/// ```compile_fail
137/// use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Recipient, User};
138///
139/// struct Queue;
140/// struct Worker;
141/// macro_rules! inert {
142/// ($actor:ty) => {
143/// impl Behavior for $actor {
144/// type Addr = MailAddr;
145/// type Msg = u8;
146/// type Event = User<MailAddr, u8>;
147/// type Sends = Vec<Never>;
148/// type Ph = Never;
149/// type Error = Never;
150/// type Birth = NoBirths;
151/// fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
152/// fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> {
153/// Ok(Actions::cont())
154/// }
155/// }
156/// };
157/// }
158/// inert!(Queue);
159/// inert!(Worker);
160///
161/// let worker = Recipient::<Worker>::global(MailAddr(1));
162/// let _: Delivery<Queue> = Delivery::new(worker, 7);
163/// ```
164///
165/// A destination also fixes its message and address namespaces:
166///
167/// ```compile_fail
168/// use behavior::{Actions, Address, Behavior, Delivery, MailAddr, Never, NoBirths, Recipient, User};
169/// #[derive(Clone, Copy, PartialEq, Eq)]
170/// struct OtherAddr(u64);
171/// impl Address for OtherAddr {
172/// type Nonce = u64;
173/// fn birth(self, nonce: u64) -> Self { Self(self.0 ^ nonce) }
174/// }
175/// struct Worker;
176/// impl Behavior for Worker {
177/// type Addr = MailAddr;
178/// type Msg = u8;
179/// type Event = User<MailAddr, u8>;
180/// type Sends = Vec<Never>;
181/// type Ph = Never;
182/// type Error = Never;
183/// type Birth = NoBirths;
184/// fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
185/// fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
186/// }
187/// let _ = Recipient::<Worker>::global(OtherAddr(1));
188/// ```
189///
190/// ```compile_fail
191/// # use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Recipient, User};
192/// # struct Worker;
193/// # impl Behavior for Worker {
194/// # type Addr = MailAddr;
195/// # type Msg = u8;
196/// # type Event = User<MailAddr, u8>;
197/// # type Sends = Vec<Never>;
198/// # type Ph = Never;
199/// # type Error = Never;
200/// # type Birth = NoBirths;
201/// # fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
202/// # fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
203/// # }
204/// let worker = Recipient::<Worker>::global(MailAddr(1));
205/// let _ = Delivery::<Worker>::new(worker, "wrong payload");
206/// ```
207pub struct Delivery<B: Behavior> {
208 pub to: Recipient<B>,
209 pub message: B::Msg,
210}
211
212impl<B: Behavior> Delivery<B> {
213 #[must_use]
214 pub fn new(to: Recipient<B>, message: B::Msg) -> Self {
215 Self { to, message }
216 }
217}
218
219impl<B> Clone for Delivery<B>
220where
221 B: Behavior,
222 B::Msg: Clone,
223{
224 fn clone(&self) -> Self {
225 Self::new(self.to, self.message.clone())
226 }
227}
228
229impl<B> PartialEq for Delivery<B>
230where
231 B: Behavior,
232 B::Msg: PartialEq,
233{
234 fn eq(&self, other: &Self) -> bool {
235 self.to == other.to && self.message == other.message
236 }
237}
238
239impl<B> Eq for Delivery<B>
240where
241 B: Behavior,
242 B::Msg: Eq,
243{
244}