Skip to main content

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    pub(crate) fn is_child(self, expected: <B::Addr as Address>::Nonce) -> bool {
100        matches!(self.route, Route::Child(nonce) if nonce == expected)
101    }
102
103    const fn from_route(route: Route<B::Addr>) -> Self {
104        Self {
105            route,
106            protocol: PhantomData,
107        }
108    }
109}
110
111impl<B: Behavior> PartialEq for Recipient<B> {
112    fn eq(&self, other: &Self) -> bool {
113        self.route == other.route
114    }
115}
116
117impl<B: Behavior> Eq for Recipient<B> {}
118
119impl<B: Behavior> core::fmt::Debug for Recipient<B>
120where
121    B::Addr: core::fmt::Debug,
122    <B::Addr as Address>::Nonce: core::fmt::Debug,
123{
124    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
125        self.route.fmt(f)
126    }
127}
128
129/// One pure communication addressed to a concrete behavior protocol.
130///
131/// Protocol identity is not inferred from the payload. Consequently, two
132/// behaviors accepting the same address and message types still have distinct
133/// delivery types.
134///
135/// ```compile_fail
136/// use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Recipient, User};
137///
138/// struct Queue;
139/// struct Worker;
140/// macro_rules! inert {
141///     ($actor:ty) => {
142///         impl Behavior for $actor {
143///             type Addr = MailAddr;
144///             type Msg = u8;
145///             type Event = User<MailAddr, u8>;
146///             type Sends = Vec<Never>;
147///             type Ph = Never;
148///             type Error = Never;
149///             type Birth = NoBirths;
150///             fn init(&mut self) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
151///             fn transition(&mut self, _: Self::Event) -> behavior::BehaviorActed<Self> {
152///                 Ok(Actions::cont())
153///             }
154///         }
155///     };
156/// }
157/// inert!(Queue);
158/// inert!(Worker);
159///
160/// let worker = Recipient::<Worker>::global(MailAddr(1));
161/// let _: Delivery<Queue> = Delivery::new(worker, 7);
162/// ```
163///
164/// A destination also fixes its message and address namespaces:
165///
166/// ```compile_fail
167/// use behavior::{Actions, Address, Behavior, Delivery, MailAddr, Never, NoBirths, Recipient, User};
168/// #[derive(Clone, Copy, PartialEq, Eq)]
169/// struct OtherAddr(u64);
170/// impl Address for OtherAddr {
171///     type Nonce = u64;
172///     fn birth(self, nonce: u64) -> Self { Self(self.0 ^ nonce) }
173/// }
174/// struct Worker;
175/// impl Behavior for Worker {
176///     type Addr = MailAddr;
177///     type Msg = u8;
178///     type Event = User<MailAddr, u8>;
179///     type Sends = Vec<Never>;
180///     type Ph = Never;
181///     type Error = Never;
182///     type Birth = NoBirths;
183///     fn init(&mut self) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
184///     fn transition(&mut self, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
185/// }
186/// let _ = Recipient::<Worker>::global(OtherAddr(1));
187/// ```
188///
189/// ```compile_fail
190/// # use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Recipient, User};
191/// # struct Worker;
192/// # impl Behavior for Worker {
193/// #     type Addr = MailAddr;
194/// #     type Msg = u8;
195/// #     type Event = User<MailAddr, u8>;
196/// #     type Sends = Vec<Never>;
197/// #     type Ph = Never;
198/// #     type Error = Never;
199/// #     type Birth = NoBirths;
200/// #     fn init(&mut self) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
201/// #     fn transition(&mut self, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
202/// # }
203/// let worker = Recipient::<Worker>::global(MailAddr(1));
204/// let _ = Delivery::<Worker>::new(worker, "wrong payload");
205/// ```
206pub struct Delivery<B: Behavior> {
207    pub to: Recipient<B>,
208    pub message: B::Msg,
209}
210
211impl<B: Behavior> Delivery<B> {
212    #[must_use]
213    pub fn new(to: Recipient<B>, message: B::Msg) -> Self {
214        Self { to, message }
215    }
216}
217
218impl<B> Clone for Delivery<B>
219where
220    B: Behavior,
221    B::Msg: Clone,
222{
223    fn clone(&self) -> Self {
224        Self::new(self.to, self.message.clone())
225    }
226}
227
228impl<B> PartialEq for Delivery<B>
229where
230    B: Behavior,
231    B::Msg: PartialEq,
232{
233    fn eq(&self, other: &Self) -> bool {
234        self.to == other.to && self.message == other.message
235    }
236}
237
238impl<B> Eq for Delivery<B>
239where
240    B: Behavior,
241    B::Msg: Eq,
242{
243}