Skip to main content

behavior/actor/
addressing.rs

1//! Protocol-indexed actor recipients and deliveries.
2
3use core::marker::PhantomData;
4
5use crate::{ActionItem, Behavior, MessageProtocol, Never, Protocol};
6
7/// A pure logical actor-address namespace.
8///
9/// An address names a transport or resolution domain. It does not allocate an
10/// actor and it does not prove that an actor is installed. Fresh allocation is
11/// interpreter-owned; the creator-local [`Address::Nonce`] is correlation
12/// evidence only and is deliberately not convertible into an address here.
13pub trait Address: Copy + Eq {
14    type Nonce: Copy + Eq;
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct MailAddr(pub u64);
19
20impl From<u64> for MailAddr {
21    fn from(value: u64) -> Self {
22        Self(value)
23    }
24}
25
26impl From<MailAddr> for u64 {
27    fn from(value: MailAddr) -> Self {
28        value.0
29    }
30}
31
32impl Address for MailAddr {
33    type Nonce = u64;
34}
35
36/// Runtime-owned exact endpoint family for one logical address namespace.
37///
38/// A runtime implements this trait on its own address newtype, selecting one
39/// statically projected endpoint representation for each concrete protocol.
40/// The projection may reuse a representation; [`EstablishedRecipient<P>`]
41/// still preserves `P` and prevents cross-protocol substitution. Ordinary
42/// protocols continue to declare only their canonical [`Protocol::Addr`] and
43/// [`Protocol::Msg`]; they never author endpoint keys or endpoint associated
44/// types. An endpoint is cloneable acquaintance evidence, but it is not
45/// intrinsically `Send`: thread transfer is required only by the concrete
46/// asynchronous interpretation boundary that performs it.
47///
48/// A local endpoint remains valid, but cannot enter the sendable
49/// [`crate::InterpretSends`] path:
50///
51/// ```compile_fail
52/// use behavior::{
53///     Address, EndpointAddress, EstablishedDelivery, EstablishedRecipient,
54///     Here, InterpretItem, InterpretSends, ItemSettlement, Never, Protocol, User,
55/// };
56/// use core::marker::PhantomData;
57/// use std::rc::Rc;
58/// #[derive(Clone, Copy, PartialEq, Eq)]
59/// struct LocalAddr(u8);
60/// impl Address for LocalAddr { type Nonce = u8; }
61/// struct LocalEndpoint<P>(Rc<()>, PhantomData<fn() -> P>);
62/// impl<P> Clone for LocalEndpoint<P> {
63///     fn clone(&self) -> Self { Self(self.0.clone(), PhantomData) }
64/// }
65/// impl EndpointAddress for LocalAddr {
66///     type Established<P> = LocalEndpoint<P> where P: Protocol<Addr = Self>;
67/// }
68/// struct LocalProtocol;
69/// impl Protocol for LocalProtocol {
70///     type Addr = LocalAddr;
71///     type Msg = Rc<()>;
72/// }
73/// struct Runtime;
74/// impl<RootEvent, Path> InterpretItem<EstablishedDelivery<LocalProtocol>, RootEvent, Path>
75///     for Runtime
76/// {
77///     fn interpret_item(
78///         &mut self,
79///         delivery: EstablishedDelivery<LocalProtocol>,
80///     ) -> impl core::future::Future<Output = ItemSettlement<
81///         EstablishedDelivery<LocalProtocol>, (), Never, Never,
82///     >> + Send {
83///         async move {
84///             drop(delivery);
85///             ItemSettlement::Accepted(())
86///         }
87///     }
88/// }
89/// fn require_async<T>()
90/// where
91///     T: InterpretSends<Runtime, User<LocalAddr, Rc<()>>, Here>,
92/// {}
93/// let endpoint = LocalEndpoint(Rc::new(()), PhantomData);
94/// let recipient = EstablishedRecipient::<LocalProtocol>::issued(endpoint);
95/// let _delivery = EstablishedDelivery::new(recipient, Rc::new(()));
96/// require_async::<Vec<EstablishedDelivery<LocalProtocol>>>();
97/// ```
98pub trait EndpointAddress: Address + Sized {
99    type Established<P>: Clone
100    where
101        P: Protocol<Addr = Self>;
102}
103
104/// Pure logical destination for one concrete protocol signature.
105///
106/// The destination protocol owner is part of the type even when two protocols
107/// share the same address namespace and message type. This value proves only
108/// the static signature at a logical address; it does not prove that an exact
109/// executable incarnation has been installed there. Addressed recipients are
110/// retained for genuine transport and name-resolution boundaries.
111pub struct Recipient<P: Protocol> {
112    address: P::Addr,
113    protocol: PhantomData<fn() -> P>,
114}
115
116impl<P: Protocol> Copy for Recipient<P> {}
117
118impl<P: Protocol> Clone for Recipient<P> {
119    fn clone(&self) -> Self {
120        *self
121    }
122}
123
124impl<P: Protocol> Recipient<P> {
125    #[must_use]
126    pub fn global(address: P::Addr) -> Self {
127        Self::new(address)
128    }
129
130    /// Return the logical address, independent of any sending actor.
131    #[must_use]
132    pub const fn address(self) -> P::Addr {
133        self.address
134    }
135
136    #[doc(hidden)]
137    const fn new(address: P::Addr) -> Self {
138        Self {
139            address,
140            protocol: PhantomData,
141        }
142    }
143}
144
145impl<A: Address, M> From<A> for Recipient<MessageProtocol<A, M>> {
146    fn from(address: A) -> Self {
147        Self::new(address)
148    }
149}
150
151/// Inert capability for one exact installed incarnation of protocol `P`.
152///
153/// The endpoint type is selected by `P::Addr`, so `P` remains the only
154/// protocol identity and ordinary domain types carry no endpoint parameter.
155/// The endpoint has no direct accessor or send method. It crosses only an
156/// explicit interpretation boundary. That boundary is public and therefore a
157/// deliberate power-user authority boundary, not exclusive runtime authority.
158///
159/// ```compile_fail
160/// use behavior::{Address, EndpointAddress, EstablishedRecipient, Protocol};
161/// #[derive(Clone, Copy, PartialEq, Eq)]
162/// struct RuntimeAddr(u64);
163/// impl Address for RuntimeAddr { type Nonce = u64; }
164/// struct Worker;
165/// impl Protocol for Worker { type Addr = RuntimeAddr; type Msg = (); }
166/// #[derive(Clone)]
167/// struct Endpoint;
168/// impl EndpointAddress for RuntimeAddr {
169///     type Established<P> = Endpoint where P: Protocol<Addr = Self>;
170/// }
171/// let recipient = EstablishedRecipient::<Worker>::issued(Endpoint);
172/// let _endpoint = recipient.endpoint();
173/// ```
174pub struct EstablishedRecipient<P>
175where
176    P: Protocol,
177    P::Addr: EndpointAddress,
178{
179    pub(crate) endpoint: <P::Addr as EndpointAddress>::Established<P>,
180}
181
182impl<P> EstablishedRecipient<P>
183where
184    P: Protocol,
185    P::Addr: EndpointAddress,
186{
187    /// Issue a capability from an endpoint already established by an
188    /// interpreter.
189    ///
190    /// This constructor performs no allocation or validation. Callers at this
191    /// power-user boundary must issue it only after successful fresh
192    /// installation and commit.
193    #[must_use]
194    pub const fn issued(endpoint: <P::Addr as EndpointAddress>::Established<P>) -> Self {
195        Self { endpoint }
196    }
197
198    /// Transfer the endpoint through an explicit interpretation boundary.
199    pub fn interpret<I>(self, interpreter: &mut I) -> I::Output
200    where
201        I: InterpretEstablished<P>,
202    {
203        interpreter.interpret_established(self.endpoint)
204    }
205}
206
207/// Public power-user transfer boundary for one exact endpoint.
208pub trait InterpretEstablished<P>
209where
210    P: Protocol,
211    P::Addr: EndpointAddress,
212{
213    type Output;
214
215    fn interpret_established(
216        &mut self,
217        endpoint: <P::Addr as EndpointAddress>::Established<P>,
218    ) -> Self::Output;
219}
220
221impl<P> Clone for EstablishedRecipient<P>
222where
223    P: Protocol,
224    P::Addr: EndpointAddress,
225{
226    fn clone(&self) -> Self {
227        Self::issued(self.endpoint.clone())
228    }
229}
230
231impl<P> core::fmt::Debug for EstablishedRecipient<P>
232where
233    P: Protocol,
234    P::Addr: EndpointAddress,
235    <P::Addr as EndpointAddress>::Established<P>: core::fmt::Debug,
236{
237    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
238        formatter
239            .debug_tuple("EstablishedRecipient")
240            .field(&self.endpoint)
241            .finish()
242    }
243}
244
245impl<P> PartialEq for EstablishedRecipient<P>
246where
247    P: Protocol,
248    P::Addr: EndpointAddress,
249    <P::Addr as EndpointAddress>::Established<P>: PartialEq,
250{
251    fn eq(&self, other: &Self) -> bool {
252        self.endpoint == other.endpoint
253    }
254}
255
256impl<P> Eq for EstablishedRecipient<P>
257where
258    P: Protocol,
259    P::Addr: EndpointAddress,
260    <P::Addr as EndpointAddress>::Established<P>: Eq,
261{
262}
263
264/// Inert capability for one exact installed concrete behavior.
265///
266/// An established recipient proves the public protocol endpoint. This value
267/// additionally preserves which concrete behavior was installed, allowing
268/// lifecycle effects to require static evidence about that behavior's event
269/// algebra. It remains inert: no direct send, shutdown, endpoint accessor, or
270/// other ambient effect is exposed.
271pub struct EstablishedActor<B>
272where
273    B: Behavior,
274    <B::Protocol as Protocol>::Addr: EndpointAddress,
275{
276    recipient: EstablishedRecipient<B::Protocol>,
277    behavior: PhantomData<fn() -> B>,
278}
279
280impl<B> EstablishedActor<B>
281where
282    B: Behavior,
283    <B::Protocol as Protocol>::Addr: EndpointAddress,
284{
285    /// Issue an exact actor capability after successful installation.
286    ///
287    /// This is the same public power-user boundary as
288    /// [`EstablishedRecipient::issued`]. It performs no allocation,
289    /// initialization, installation, or validation.
290    #[must_use]
291    pub const fn issued(
292        endpoint: <<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>,
293    ) -> Self {
294        Self {
295            recipient: EstablishedRecipient::issued(endpoint),
296            behavior: PhantomData,
297        }
298    }
299
300    pub(crate) const fn from_recipient(recipient: EstablishedRecipient<B::Protocol>) -> Self {
301        Self {
302            recipient,
303            behavior: PhantomData,
304        }
305    }
306
307    /// Project the exact public-protocol recipient for this incarnation.
308    #[must_use]
309    pub fn recipient(&self) -> EstablishedRecipient<B::Protocol> {
310        self.recipient.clone()
311    }
312
313    /// Consume the concrete-actor proof and retain its exact protocol
314    /// recipient.
315    #[must_use]
316    pub fn into_recipient(self) -> EstablishedRecipient<B::Protocol> {
317        self.recipient
318    }
319
320    /// Transfer the endpoint through the explicit interpretation boundary
321    /// while preserving `B` in the caller's request type.
322    pub fn interpret<I>(self, interpreter: &mut I) -> I::Output
323    where
324        I: InterpretEstablished<B::Protocol>,
325    {
326        self.recipient.interpret(interpreter)
327    }
328}
329
330impl<B> Clone for EstablishedActor<B>
331where
332    B: Behavior,
333    <B::Protocol as Protocol>::Addr: EndpointAddress,
334{
335    fn clone(&self) -> Self {
336        Self {
337            recipient: self.recipient.clone(),
338            behavior: PhantomData,
339        }
340    }
341}
342
343impl<B> core::fmt::Debug for EstablishedActor<B>
344where
345    B: Behavior,
346    <B::Protocol as Protocol>::Addr: EndpointAddress,
347    <<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>:
348        core::fmt::Debug,
349{
350    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
351        formatter
352            .debug_tuple("EstablishedActor")
353            .field(&self.recipient)
354            .finish()
355    }
356}
357
358impl<B> PartialEq for EstablishedActor<B>
359where
360    B: Behavior,
361    <B::Protocol as Protocol>::Addr: EndpointAddress,
362    <<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>: PartialEq,
363{
364    fn eq(&self, other: &Self) -> bool {
365        self.recipient == other.recipient
366    }
367}
368
369impl<B> Eq for EstablishedActor<B>
370where
371    B: Behavior,
372    <B::Protocol as Protocol>::Addr: EndpointAddress,
373    <<B::Protocol as Protocol>::Addr as EndpointAddress>::Established<B::Protocol>: Eq,
374{
375}
376
377impl<P: Protocol> PartialEq for Recipient<P> {
378    fn eq(&self, other: &Self) -> bool {
379        self.address == other.address
380    }
381}
382
383impl<P: Protocol> Eq for Recipient<P> {}
384
385impl<P: Protocol> core::fmt::Debug for Recipient<P>
386where
387    P::Addr: core::fmt::Debug,
388    <P::Addr as Address>::Nonce: core::fmt::Debug,
389{
390    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
391        self.address.fmt(f)
392    }
393}
394
395/// One pure communication addressed to a concrete protocol signature.
396///
397/// Protocol identity is not inferred from the payload. Consequently, two
398/// protocols with the same address and message types still have distinct
399/// delivery types.
400///
401/// ```compile_fail
402/// use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Protocol, Recipient, User};
403///
404/// struct Queue;
405/// struct Worker;
406/// macro_rules! inert {
407///     ($actor:ty) => {
408///         impl Protocol for $actor {
409///             type Addr = MailAddr;
410///             type Msg = u8;
411///         }
412///         impl Behavior for $actor {
413///             type Event = User<MailAddr, u8>;
414///             type Sends = Vec<Never>;
415///             type Ph = Never;
416///             type Error = Never;
417///             type Birth = NoBirths;
418///             fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
419///             fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> {
420///                 Ok(Actions::cont())
421///             }
422///         }
423///     };
424/// }
425/// inert!(Queue);
426/// inert!(Worker);
427///
428/// let worker = Recipient::<Worker>::global(MailAddr(1));
429/// let _: Delivery<Queue> = Delivery::new(worker, 7);
430/// ```
431///
432/// A destination also fixes its message and address namespaces:
433///
434/// ```compile_fail
435/// use behavior::{Actions, Address, Behavior, Delivery, MailAddr, Never, NoBirths, Protocol, Recipient, User};
436/// #[derive(Clone, Copy, PartialEq, Eq)]
437/// struct OtherAddr(u64);
438/// impl Address for OtherAddr {
439///     type Nonce = u64;
440/// }
441/// struct Worker;
442/// impl Protocol for Worker {
443///     type Addr = MailAddr;
444///     type Msg = u8;
445/// }
446/// impl Behavior for Worker {
447///     type Event = User<MailAddr, u8>;
448///     type Sends = Vec<Never>;
449///     type Ph = Never;
450///     type Error = Never;
451///     type Birth = NoBirths;
452///     fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
453///     fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
454/// }
455/// let _ = Recipient::<Worker>::global(OtherAddr(1));
456/// ```
457///
458/// ```compile_fail
459/// # use behavior::{Actions, Behavior, Delivery, MailAddr, Never, NoBirths, Protocol, Recipient, User};
460/// # struct Worker;
461/// # impl Protocol for Worker {
462/// #     type Addr = MailAddr;
463/// #     type Msg = u8;
464/// # }
465/// # impl Behavior for Worker {
466/// #     type Protocol = Self;
467/// #     type Event = User<MailAddr, u8>;
468/// #     type Sends = Vec<Never>;
469/// #     type Ph = Never;
470/// #     type Error = Never;
471/// #     type Birth = NoBirths;
472/// #     fn init(&mut self, _: crate::InitializationTurn) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
473/// #     fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> { Ok(Actions::cont()) }
474/// # }
475/// let worker = Recipient::<Worker>::global(MailAddr(1));
476/// let _ = Delivery::<Worker>::new(worker, "wrong payload");
477/// ```
478/// Exact reason one logical delivery was not accepted.
479#[derive(Clone, Copy, Debug, Eq, PartialEq)]
480pub enum LogicalDeliveryReason {
481    UnknownAddress,
482    ClosedRecipient,
483}
484
485/// Exact reason one established delivery was not accepted.
486#[derive(Clone, Copy, Debug, Eq, PartialEq)]
487pub enum ExactDeliveryReason {
488    ClosedRecipient,
489}
490
491pub struct Delivery<P: Protocol> {
492    pub to: Recipient<P>,
493    pub message: P::Msg,
494}
495
496impl<P: Protocol> Delivery<P> {
497    #[must_use]
498    pub fn new(to: Recipient<P>, message: P::Msg) -> Self {
499        Self { to, message }
500    }
501}
502
503impl<P> Clone for Delivery<P>
504where
505    P: Protocol,
506    P::Msg: Clone,
507{
508    fn clone(&self) -> Self {
509        Self {
510            to: self.to,
511            message: self.message.clone(),
512        }
513    }
514}
515
516impl<P> PartialEq for Delivery<P>
517where
518    P: Protocol,
519    P::Msg: PartialEq,
520{
521    fn eq(&self, other: &Self) -> bool {
522        self.to == other.to && self.message == other.message
523    }
524}
525
526impl<P> Eq for Delivery<P>
527where
528    P: Protocol,
529    P::Msg: Eq,
530{
531}
532
533impl<P> ActionItem for Delivery<P>
534where
535    P: Protocol,
536    P::Addr: Send,
537    P::Msg: Send,
538{
539    type Accepted = ();
540    type Rejection = LogicalDeliveryReason;
541    type Prerequisite = Never;
542}
543
544/// One pure communication to an exact installed incarnation.
545///
546/// Unlike [`Delivery`], this effect carries the runtime-issued endpoint and
547/// requires no address-to-endpoint resolution. Constructing it remains pure;
548/// only an explicit [`crate::InterpretItem`] implementation for this exact
549/// delivery type can
550/// perform the communication.
551///
552/// Exact endpoints remain protocol-indexed even when two protocols share an
553/// address namespace and message type:
554///
555/// ```compile_fail
556/// use behavior::{
557///     Address, EndpointAddress, EstablishedDelivery, EstablishedRecipient,
558///     Protocol,
559/// };
560/// use core::marker::PhantomData;
561/// #[derive(Clone, Copy, PartialEq, Eq)]
562/// struct RuntimeAddr(u64);
563/// impl Address for RuntimeAddr { type Nonce = u64; }
564/// struct Endpoint<P>(PhantomData<fn() -> P>);
565/// impl<P> Clone for Endpoint<P> {
566///     fn clone(&self) -> Self { Self(PhantomData) }
567/// }
568/// impl EndpointAddress for RuntimeAddr {
569///     type Established<P> = Endpoint<P> where P: Protocol<Addr = Self>;
570/// }
571/// struct Queue;
572/// struct Worker;
573/// impl Protocol for Queue { type Addr = RuntimeAddr; type Msg = u8; }
574/// impl Protocol for Worker { type Addr = RuntimeAddr; type Msg = u8; }
575/// let worker = EstablishedRecipient::<Worker>::issued(Endpoint(PhantomData));
576/// let _: EstablishedDelivery<Queue> = EstablishedDelivery::new(worker, 7);
577/// ```
578pub struct EstablishedDelivery<P>
579where
580    P: Protocol,
581    P::Addr: EndpointAddress,
582{
583    pub to: EstablishedRecipient<P>,
584    pub message: P::Msg,
585}
586
587impl<P> EstablishedDelivery<P>
588where
589    P: Protocol,
590    P::Addr: EndpointAddress,
591{
592    #[must_use]
593    pub const fn new(to: EstablishedRecipient<P>, message: P::Msg) -> Self {
594        Self { to, message }
595    }
596}
597
598impl<P> Clone for EstablishedDelivery<P>
599where
600    P: Protocol,
601    P::Addr: EndpointAddress,
602    P::Msg: Clone,
603{
604    fn clone(&self) -> Self {
605        Self::new(self.to.clone(), self.message.clone())
606    }
607}
608
609impl<P> core::fmt::Debug for EstablishedDelivery<P>
610where
611    P: Protocol,
612    P::Addr: EndpointAddress,
613    EstablishedRecipient<P>: core::fmt::Debug,
614    P::Msg: core::fmt::Debug,
615{
616    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
617        formatter
618            .debug_struct("EstablishedDelivery")
619            .field("to", &self.to)
620            .field("message", &self.message)
621            .finish()
622    }
623}
624
625impl<P> PartialEq for EstablishedDelivery<P>
626where
627    P: Protocol,
628    P::Addr: EndpointAddress,
629    EstablishedRecipient<P>: PartialEq,
630    P::Msg: PartialEq,
631{
632    fn eq(&self, other: &Self) -> bool {
633        self.to == other.to && self.message == other.message
634    }
635}
636
637impl<P> Eq for EstablishedDelivery<P>
638where
639    P: Protocol,
640    P::Addr: EndpointAddress,
641    EstablishedRecipient<P>: Eq,
642    P::Msg: Eq,
643{
644}
645
646impl<P> ActionItem for EstablishedDelivery<P>
647where
648    P: Protocol,
649    P::Addr: EndpointAddress,
650    <P::Addr as EndpointAddress>::Established<P>: Send,
651    P::Msg: Send,
652{
653    type Accepted = ();
654    type Rejection = ExactDeliveryReason;
655    type Prerequisite = Never;
656}
657
658#[cfg(test)]
659mod tests {
660    use super::*;
661    use crate::{Actions, Never, NoBirths, User};
662    use std::rc::Rc;
663
664    struct Inbox;
665
666    struct SignatureOnly;
667
668    #[derive(Clone, Copy, PartialEq, Eq)]
669    struct LocalAddr(u8);
670
671    impl Address for LocalAddr {
672        type Nonce = u8;
673    }
674
675    struct LocalProtocol;
676
677    impl Protocol for LocalProtocol {
678        type Addr = LocalAddr;
679        type Msg = u8;
680    }
681
682    struct LocalEndpoint<P> {
683        token: Rc<()>,
684        protocol: PhantomData<fn() -> P>,
685    }
686
687    impl<P> Clone for LocalEndpoint<P> {
688        fn clone(&self) -> Self {
689            Self {
690                token: self.token.clone(),
691                protocol: PhantomData,
692            }
693        }
694    }
695
696    impl<P> core::fmt::Debug for LocalEndpoint<P> {
697        fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
698            formatter.write_str("LocalEndpoint")
699        }
700    }
701
702    impl<P> PartialEq for LocalEndpoint<P> {
703        fn eq(&self, other: &Self) -> bool {
704            Rc::ptr_eq(&self.token, &other.token)
705        }
706    }
707
708    impl<P> Eq for LocalEndpoint<P> {}
709
710    impl EndpointAddress for LocalAddr {
711        type Established<P>
712            = LocalEndpoint<P>
713        where
714            P: Protocol<Addr = Self>;
715    }
716
717    struct LocalTransfer;
718
719    impl InterpretEstablished<LocalProtocol> for LocalTransfer {
720        type Output = Rc<()>;
721
722        fn interpret_established(
723            &mut self,
724            endpoint: LocalEndpoint<LocalProtocol>,
725        ) -> Self::Output {
726            endpoint.token
727        }
728    }
729
730    struct LocalBehavior;
731
732    impl Behavior for LocalBehavior {
733        type Protocol = LocalProtocol;
734        type Event = User<LocalAddr, u8>;
735        type Sends = Vec<Never>;
736        type Ph = Never;
737        type Error = Never;
738        type Birth = NoBirths;
739
740        fn transition(
741            &mut self,
742            _: crate::ActiveTurn,
743            _: Self::Event,
744        ) -> crate::BehaviorActed<Self> {
745            Ok(Actions::cont())
746        }
747    }
748
749    impl crate::Protocol for SignatureOnly {
750        type Addr = MailAddr;
751        type Msg = u8;
752    }
753
754    impl behavior::Protocol for Inbox {
755        type Addr = MailAddr;
756        type Msg = u8;
757    }
758
759    impl Behavior for Inbox {
760        type Protocol = Self;
761        type Event = User<MailAddr, u8>;
762        type Sends = Vec<Never>;
763        type Ph = Never;
764        type Error = Never;
765        type Birth = NoBirths;
766
767        fn init(&mut self, _: crate::InitializationTurn) -> crate::BehaviorActed<Self> {
768            Ok(Actions::cont())
769        }
770
771        fn transition(
772            &mut self,
773            _: crate::ActiveTurn,
774            _: Self::Event,
775        ) -> crate::BehaviorActed<Self> {
776            Ok(Actions::cont())
777        }
778    }
779
780    #[test]
781    fn mail_address_conversion_preserves_nonzero_value() {
782        assert_eq!(u64::from(MailAddr(41)), 41);
783    }
784
785    #[test]
786    fn routing_requires_only_the_static_protocol_signature() {
787        let recipient = Recipient::<SignatureOnly>::global(MailAddr(7));
788        let delivery = Delivery::new(recipient, 11);
789
790        assert_eq!(delivery.to.address(), MailAddr(7));
791        assert_eq!(delivery.message, 11);
792    }
793
794    #[test]
795    fn recipient_value_contract_distinguishes_logical_addresses() {
796        let global = Recipient::<Inbox>::global(MailAddr(7));
797        let same_global = Recipient::<Inbox>::global(MailAddr(7));
798        let other_global = Recipient::<Inbox>::global(MailAddr(8));
799
800        assert_eq!(global, same_global);
801        assert_ne!(global, other_global);
802        assert_eq!(global.address(), MailAddr(7));
803        assert_eq!(format!("{global:?}"), "MailAddr(7)");
804    }
805
806    #[test]
807    fn established_capability_accepts_a_local_non_send_endpoint() {
808        let token = Rc::new(());
809        let recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
810            token: token.clone(),
811            protocol: PhantomData,
812        });
813        let retained = recipient.clone();
814        let extracted = retained.interpret(&mut LocalTransfer);
815
816        assert!(Rc::ptr_eq(&token, &extracted));
817        drop(recipient);
818    }
819
820    #[test]
821    fn established_values_compare_complete_identity_and_expose_debug_shape() {
822        let token = Rc::new(());
823        let other_token = Rc::new(());
824        let recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
825            token: token.clone(),
826            protocol: PhantomData,
827        });
828        let same_recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
829            token,
830            protocol: PhantomData,
831        });
832        let other_recipient = EstablishedRecipient::<LocalProtocol>::issued(LocalEndpoint {
833            token: other_token,
834            protocol: PhantomData,
835        });
836
837        assert_eq!(recipient, same_recipient);
838        assert_ne!(recipient, other_recipient);
839        assert_eq!(
840            format!("{recipient:?}"),
841            "EstablishedRecipient(LocalEndpoint)"
842        );
843
844        let actor = EstablishedActor::<LocalBehavior>::from_recipient(recipient.clone());
845        let same_actor = EstablishedActor::<LocalBehavior>::from_recipient(same_recipient.clone());
846        let other_actor =
847            EstablishedActor::<LocalBehavior>::from_recipient(other_recipient.clone());
848        assert_eq!(actor, same_actor);
849        assert_ne!(actor, other_actor);
850        assert_eq!(
851            format!("{actor:?}"),
852            "EstablishedActor(EstablishedRecipient(LocalEndpoint))"
853        );
854
855        let delivery = EstablishedDelivery::new(recipient, 7);
856        let same_delivery = EstablishedDelivery::new(same_recipient.clone(), 7);
857        let other_destination = EstablishedDelivery::new(other_recipient.clone(), 7);
858        let other_message = EstablishedDelivery::new(same_recipient, 9);
859        let both_different = EstablishedDelivery::new(other_recipient, 9);
860        assert_eq!(delivery, same_delivery);
861        assert_ne!(delivery, other_destination);
862        assert_ne!(delivery, other_message);
863        assert_ne!(delivery, both_different);
864        assert_eq!(
865            format!("{delivery:?}"),
866            "EstablishedDelivery { to: EstablishedRecipient(LocalEndpoint), message: 7 }"
867        );
868    }
869
870    #[test]
871    fn delivery_equality_requires_both_destination_and_message() {
872        let value = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
873        let same = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 5);
874        let other_destination = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 5);
875        let other_message = Delivery::<Inbox>::new(Recipient::global(MailAddr(1)), 6);
876        let both_different = Delivery::<Inbox>::new(Recipient::global(MailAddr(2)), 6);
877
878        assert!(value == same);
879        assert!(value != other_destination);
880        assert!(value != other_message);
881        assert!(value != both_different);
882    }
883}