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