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    #[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}