Skip to main content

behavior/actor/
addressing.rs

1//! Protocol-indexed actor recipients and deliveries.
2
3use core::marker::PhantomData;
4
5use crate::{MessageProtocol, Protocol};
6
7#[cfg(test)]
8use crate::Behavior;
9
10/// A pure actor-address namespace.
11pub trait Address: Copy + Eq {
12    type Nonce: Copy + Eq;
13
14    #[must_use]
15    fn birth(self, nonce: Self::Nonce) -> Self;
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct MailAddr(pub u64);
20
21impl From<u64> for MailAddr {
22    fn from(value: u64) -> Self {
23        Self(value)
24    }
25}
26
27impl From<MailAddr> for u64 {
28    fn from(value: MailAddr) -> Self {
29        value.0
30    }
31}
32
33impl Address for MailAddr {
34    type Nonce = u64;
35
36    fn birth(self, nonce: u64) -> Self {
37        Self(self.0 ^ nonce.wrapping_mul(0x9E37_79B9_7F4A_7C15))
38    }
39}
40
41/// Pure established identity for one concrete destination protocol signature.
42///
43/// The destination protocol owner is part of the type even when two protocols
44/// share the same address namespace and message type. A recipient proves the
45/// static signature only; it does not prove that an executable
46/// [`Behavior`](crate::Behavior)
47/// has been installed at the address. The value contains no mailbox, endpoint,
48/// registry entry, or other interpreter-owned capability.
49///
50/// Creator-local routes are deliberately a different type and cannot be
51/// smuggled into APIs that retain established recipients:
52///
53/// ```compile_fail
54/// use behavior::{ChildRecipient, Delivery, MailAddr, Protocol};
55/// struct Destination;
56/// impl Protocol for Destination {
57///     type Addr = MailAddr;
58///     type Msg = u8;
59/// }
60/// let local = ChildRecipient::<Destination>::new(7);
61/// let _ = Delivery::new(local, 1);
62/// ```
63pub struct Recipient<P: Protocol> {
64    address: P::Addr,
65    protocol: PhantomData<fn() -> P>,
66}
67
68impl<P: Protocol> Copy for Recipient<P> {}
69
70impl<P: Protocol> Clone for Recipient<P> {
71    fn clone(&self) -> Self {
72        *self
73    }
74}
75
76impl<P: Protocol> Recipient<P> {
77    #[must_use]
78    pub fn global(address: P::Addr) -> Self {
79        Self::new(address)
80    }
81
82    /// Return the established address, independent of any sending actor.
83    #[must_use]
84    pub const fn address(self) -> P::Addr {
85        self.address
86    }
87
88    #[doc(hidden)]
89    const fn new(address: P::Addr) -> Self {
90        Self {
91            address,
92            protocol: PhantomData,
93        }
94    }
95}
96
97impl<A: Address, M> From<A> for Recipient<MessageProtocol<A, M>> {
98    fn from(address: A) -> Self {
99        Self::new(address)
100    }
101}
102
103/// A creator-local route to one staged or established direct child.
104///
105/// Unlike [`Recipient`], this value is not an actor identity and cannot be
106/// transferred as a generally usable destination. It is resolved only by the
107/// interpreter of the actor whose child namespace owns `nonce`.
108pub struct ChildRecipient<P: Protocol> {
109    nonce: <P::Addr as Address>::Nonce,
110    protocol: PhantomData<fn() -> P>,
111}
112
113impl<P: Protocol> Copy for ChildRecipient<P> {}
114impl<P: Protocol> Clone for ChildRecipient<P> {
115    fn clone(&self) -> Self {
116        *self
117    }
118}
119
120impl<P: Protocol> ChildRecipient<P> {
121    #[must_use]
122    pub const fn new(nonce: <P::Addr as Address>::Nonce) -> Self {
123        Self {
124            nonce,
125            protocol: PhantomData,
126        }
127    }
128
129    #[must_use]
130    pub const fn nonce(self) -> <P::Addr as Address>::Nonce {
131        self.nonce
132    }
133}
134
135/// Exhaustive destination of one delivery.
136pub enum DeliveryTarget<P: Protocol> {
137    Established(Recipient<P>),
138    LocalChild(ChildRecipient<P>),
139}
140
141impl<P: Protocol> Copy for DeliveryTarget<P> {}
142impl<P: Protocol> Clone for DeliveryTarget<P> {
143    fn clone(&self) -> Self {
144        *self
145    }
146}
147
148impl<P: Protocol> PartialEq for ChildRecipient<P> {
149    fn eq(&self, other: &Self) -> bool {
150        self.nonce == other.nonce
151    }
152}
153impl<P: Protocol> Eq for ChildRecipient<P> {}
154
155impl<P: Protocol> PartialEq for DeliveryTarget<P> {
156    fn eq(&self, other: &Self) -> bool {
157        match (self, other) {
158            (Self::Established(left), Self::Established(right)) => left == right,
159            (Self::LocalChild(left), Self::LocalChild(right)) => left == right,
160            _ => false,
161        }
162    }
163}
164impl<P: Protocol> Eq for DeliveryTarget<P> {}
165
166impl<P: Protocol> PartialEq<Recipient<P>> for DeliveryTarget<P> {
167    fn eq(&self, other: &Recipient<P>) -> bool {
168        matches!(self, Self::Established(recipient) if recipient == other)
169    }
170}
171
172impl<P: Protocol> core::fmt::Debug for DeliveryTarget<P>
173where
174    P::Addr: core::fmt::Debug,
175    <P::Addr as Address>::Nonce: core::fmt::Debug,
176{
177    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178        match self {
179            Self::Established(recipient) => f.debug_tuple("Established").field(recipient).finish(),
180            Self::LocalChild(child) => f.debug_tuple("LocalChild").field(&child.nonce()).finish(),
181        }
182    }
183}
184
185impl<P: Protocol> DeliveryTarget<P> {
186    #[must_use]
187    pub fn resolve(self, emitter: P::Addr) -> P::Addr {
188        match self {
189            Self::Established(recipient) => recipient.address(),
190            Self::LocalChild(child) => emitter.birth(child.nonce()),
191        }
192    }
193
194    #[doc(hidden)]
195    #[must_use]
196    pub fn is_local_child(self, expected: <P::Addr as Address>::Nonce) -> bool {
197        matches!(self, Self::LocalChild(child) if child.nonce() == expected)
198    }
199}
200
201impl<P: Protocol> PartialEq for Recipient<P> {
202    fn eq(&self, other: &Self) -> bool {
203        self.address == other.address
204    }
205}
206
207impl<P: Protocol> Eq for Recipient<P> {}
208
209impl<P: Protocol> core::fmt::Debug for Recipient<P>
210where
211    P::Addr: core::fmt::Debug,
212    <P::Addr as Address>::Nonce: core::fmt::Debug,
213{
214    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
215        self.address.fmt(f)
216    }
217}
218
219/// One pure communication addressed to a concrete protocol signature.
220///
221/// Protocol identity is not inferred from the payload. Consequently, two
222/// protocols with the same address and message types still have distinct
223/// delivery types.
224///
225/// ```compile_fail
226/// use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Protocol, Recipient, User};
227///
228/// struct Queue;
229/// struct Worker;
230/// macro_rules! inert {
231///     ($actor:ty) => {
232///         impl Protocol for $actor {
233///             type Addr = MailAddr;
234///             type Msg = u8;
235///         }
236///         impl Behavior for $actor {
237///             type Event = User<MailAddr, u8>;
238///             type Sends = Vec<Never>;
239///             type Ph = Never;
240///             type Error = Never;
241///             type Birth = NoBirths;
242///             fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
243///             fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> {
244///                 Ok(Actions::cont())
245///             }
246///         }
247///     };
248/// }
249/// inert!(Queue);
250/// inert!(Worker);
251///
252/// let worker = Recipient::<Worker>::global(MailAddr(1));
253/// let _: Delivery<Queue> = Delivery::new(worker, 7);
254/// ```
255///
256/// A destination also fixes its message and address namespaces:
257///
258/// ```compile_fail
259/// use behavior::{Actions, Address, Behavior, Delivery, MailAddr, Never, NoBirths, Protocol, Recipient, User};
260/// #[derive(Clone, Copy, PartialEq, Eq)]
261/// struct OtherAddr(u64);
262/// impl Address for OtherAddr {
263///     type Nonce = u64;
264///     fn birth(self, nonce: u64) -> Self { Self(self.0 ^ nonce) }
265/// }
266/// struct Worker;
267/// impl Protocol for Worker {
268///     type Addr = MailAddr;
269///     type Msg = u8;
270/// }
271/// impl Behavior for Worker {
272///     type Event = User<MailAddr, u8>;
273///     type Sends = Vec<Never>;
274///     type Ph = Never;
275///     type Error = Never;
276///     type Birth = NoBirths;
277///     fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
278///     fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
279/// }
280/// let _ = Recipient::<Worker>::global(OtherAddr(1));
281/// ```
282///
283/// ```compile_fail
284/// # use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Protocol, Recipient, User};
285/// # struct Worker;
286/// # impl Protocol for Worker {
287/// #     type Addr = MailAddr;
288/// #     type Msg = u8;
289/// # }
290/// # impl Behavior for Worker {
291/// #     type Protocol = Self;
292/// #     type Event = User<MailAddr, u8>;
293/// #     type Sends = Vec<Never>;
294/// #     type Ph = Never;
295/// #     type Error = Never;
296/// #     type Birth = NoBirths;
297/// #     fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
298/// #     fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
299/// # }
300/// let worker = Recipient::<Worker>::global(MailAddr(1));
301/// let _ = Delivery::<Worker>::new(worker, "wrong payload");
302/// ```
303pub struct Delivery<P: Protocol> {
304    pub to: DeliveryTarget<P>,
305    pub message: P::Msg,
306}
307
308impl<P: Protocol> Delivery<P> {
309    #[must_use]
310    pub fn new(to: Recipient<P>, message: P::Msg) -> Self {
311        Self {
312            to: DeliveryTarget::Established(to),
313            message,
314        }
315    }
316
317    #[must_use]
318    pub fn local_child(to: ChildRecipient<P>, message: P::Msg) -> Self {
319        Self {
320            to: DeliveryTarget::LocalChild(to),
321            message,
322        }
323    }
324}
325
326impl<P> Clone for Delivery<P>
327where
328    P: Protocol,
329    P::Msg: Clone,
330{
331    fn clone(&self) -> Self {
332        Self {
333            to: self.to,
334            message: self.message.clone(),
335        }
336    }
337}
338
339impl<P> PartialEq for Delivery<P>
340where
341    P: Protocol,
342    P::Msg: PartialEq,
343{
344    fn eq(&self, other: &Self) -> bool {
345        self.to == other.to && self.message == other.message
346    }
347}
348
349impl<P> Eq for Delivery<P>
350where
351    P: Protocol,
352    P::Msg: Eq,
353{
354}
355
356#[cfg(test)]
357mod tests {
358    use super::*;
359    use crate::{Actions, Never, NoBirths, User};
360
361    struct Inbox;
362
363    struct SignatureOnly;
364
365    impl crate::Protocol for SignatureOnly {
366        type Addr = MailAddr;
367        type Msg = u8;
368    }
369
370    impl behavior::Protocol for Inbox {
371        type Addr = MailAddr;
372        type Msg = u8;
373    }
374
375    impl Behavior for Inbox {
376        type Protocol = Self;
377        type Event = User<MailAddr, u8>;
378        type Sends = Vec<Never>;
379        type Ph = Never;
380        type Error = Never;
381        type Birth = NoBirths;
382
383        fn init(&mut self, _: crate::InitializationTurn) -> crate::BehaviorActed<Self> {
384            Ok(Actions::cont())
385        }
386
387        fn transition(
388            &mut self,
389            _: crate::ActiveTurn,
390            _: Self::Event,
391        ) -> crate::BehaviorActed<Self> {
392            Ok(Actions::cont())
393        }
394    }
395
396    #[test]
397    fn mail_address_conversion_preserves_nonzero_value() {
398        assert_eq!(u64::from(MailAddr(41)), 41);
399    }
400
401    #[test]
402    fn routing_requires_only_the_static_protocol_signature() {
403        let recipient = Recipient::<SignatureOnly>::global(MailAddr(7));
404        let delivery = Delivery::new(recipient, 11);
405
406        assert_eq!(delivery.to.resolve(MailAddr(99)), MailAddr(7));
407        assert_eq!(delivery.to.resolve(MailAddr(1234)), MailAddr(7));
408        assert_eq!(delivery.message, 11);
409    }
410
411    #[test]
412    fn recipient_value_contract_distinguishes_routes() {
413        let global = Recipient::<Inbox>::global(MailAddr(7));
414        let same_global = Recipient::<Inbox>::global(MailAddr(7));
415        let other_global = Recipient::<Inbox>::global(MailAddr(8));
416        let child = DeliveryTarget::<Inbox>::LocalChild(ChildRecipient::new(3));
417        let same_child = DeliveryTarget::<Inbox>::LocalChild(ChildRecipient::new(3));
418        let other_child = DeliveryTarget::<Inbox>::LocalChild(ChildRecipient::new(4));
419
420        assert_eq!(global, same_global);
421        assert_ne!(global, other_global);
422        assert_ne!(child, global);
423        assert_eq!(child, same_child);
424        assert_ne!(child, other_child);
425        assert!(child.is_local_child(3));
426        assert!(!child.is_local_child(4));
427        assert_eq!(global.address(), MailAddr(7));
428        assert_eq!(format!("{global:?}"), "MailAddr(7)");
429        assert_eq!(format!("{child:?}"), "LocalChild(3)");
430    }
431
432    #[test]
433    fn delivery_equality_requires_both_destination_and_message() {
434        let value = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
435        let same = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
436        let other_destination = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 5);
437        let other_message = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 6);
438        let both_different = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 6);
439
440        assert!(value == same);
441        assert!(value != other_destination);
442        assert!(value != other_message);
443        assert!(value != both_different);
444    }
445}