Skip to main content

behavior_actors/protocol/
mod.rs

1//! Neutral typed vocabulary for interpreter-originated event and service lanes.
2//!
3//! Concrete behavior transformations define the closed sum types that add
4//! these lanes. Keeping their values and construction capabilities here avoids
5//! dependencies between otherwise independent transformations.
6
7mod established;
8
9pub use established::{
10    CancelObservation, EstablishedChild, EstablishedObservation, EstablishedShutdownResolved,
11    InterpretEstablishedObservation, InterpretEstablishedShutdown, ObservationAuthority,
12    ObservationId, ObservationOperation, ObservationRejection, ObservationRelationship,
13    ObserveEstablished, ObserveEstablishedCreation, ShutdownEstablished, ShutdownId,
14    ShutdownRejection, established_child,
15};
16
17use std::time::Duration;
18
19use std::time::Instant;
20
21use crate::{Crash, Exit};
22
23pub use behavior::CreationRejection;
24use behavior::{
25    ActionItem, Address, InterpretationProgress, ItemSettlement, Protocol, SourceAction,
26    finish_item, prepare_item,
27};
28use behavior::{CreationId, CreationKind};
29
30/// Exact timer correlation accepted by the local scheduler.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct TimerScheduled {
33    /// Actor-local timer identity supplied by the behavior.
34    pub id: TimerId,
35    /// Behavior-owned generation returned without reinterpretation.
36    pub generation: TimerGeneration,
37}
38
39/// Exact reason an absolute timer request was rejected before scheduling.
40#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
41pub enum ScheduleAtRejection {
42    /// The timer queue cannot issue another internal generation.
43    #[error("timer queue generation exhausted")]
44    QueueGenerationExhausted,
45    /// The timer queue cannot issue another stable insertion sequence.
46    #[error("timer queue insertion sequence exhausted")]
47    QueueSequenceExhausted,
48}
49
50/// Exact reason a relative timer request was rejected before scheduling.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
52pub enum ScheduleAfterRejection {
53    /// Adding the delay to the interpreter's current instant overflowed.
54    #[error("timer deadline overflowed")]
55    DeadlineOverflow,
56    /// The timer queue cannot issue another internal generation.
57    #[error("timer queue generation exhausted")]
58    QueueGenerationExhausted,
59    /// The timer queue cannot issue another stable insertion sequence.
60    #[error("timer queue insertion sequence exhausted")]
61    QueueSequenceExhausted,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65pub struct TimerId(pub u64);
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub struct TimerGeneration(pub u64);
69
70impl From<u64> for TimerId {
71    fn from(value: u64) -> Self {
72        Self(value)
73    }
74}
75
76impl From<TimerId> for u64 {
77    fn from(value: TimerId) -> Self {
78        value.0
79    }
80}
81
82impl From<u64> for TimerGeneration {
83    fn from(value: u64) -> Self {
84        Self(value)
85    }
86}
87
88impl From<TimerGeneration> for u64 {
89    fn from(value: TimerGeneration) -> Self {
90        value.0
91    }
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub struct ScheduleAt {
96    pub id: TimerId,
97    pub generation: TimerGeneration,
98    pub at: Instant,
99}
100
101impl ScheduleAt {
102    #[must_use]
103    pub const fn new(id: TimerId, generation: TimerGeneration, at: Instant) -> Self {
104        Self { id, generation, at }
105    }
106}
107
108impl From<(TimerId, TimerGeneration, Instant)> for ScheduleAt {
109    fn from((id, generation, at): (TimerId, TimerGeneration, Instant)) -> Self {
110        Self::new(id, generation, at)
111    }
112}
113
114impl behavior::InterpreterRequest for ScheduleAt {
115    type ReturnToEmitter = behavior::ReturnsToEmitter<TimerElapsed, behavior::Here>;
116    type LogicalProtocols = behavior::NoBirthProtocols;
117}
118
119impl ActionItem for ScheduleAt {
120    type Custody = (Option<Self>, Option<Self::Reply>);
121    type Input<'a>
122        = &'a mut Option<Self>
123    where
124        Self: 'a;
125    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
126
127    fn prepare_interpretation(
128        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
129    ) {
130        prepare_item::<Self>(progress);
131    }
132
133    fn interpretation_input<'a>(
134        custody: &'a mut Self::Custody,
135    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
136    where
137        Self: 'a,
138    {
139        match custody {
140            (input @ Some(_), received @ None) => Some((input, received)),
141            _ => None,
142        }
143    }
144
145    fn finish_interpretation(
146        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
147    ) {
148        finish_item::<Self>(progress);
149    }
150
151    type Accepted = TimerScheduled;
152    type Rejection = ScheduleAtRejection;
153    type Prerequisite = behavior::Never;
154}
155
156impl SourceAction for ScheduleAt {
157    type Source = Self;
158}
159
160/// Request scheduling relative to the interpreter's clock.
161///
162/// Constructing this value does not observe a clock. The interpreter resolves
163/// `after` only when it interprets the successful transition that emitted the
164/// request.
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub struct ScheduleAfter {
167    pub id: TimerId,
168    pub generation: TimerGeneration,
169    pub after: Duration,
170}
171
172impl ScheduleAfter {
173    #[must_use]
174    pub const fn new(id: TimerId, generation: TimerGeneration, after: Duration) -> Self {
175        Self {
176            id,
177            generation,
178            after,
179        }
180    }
181}
182
183impl From<(TimerId, TimerGeneration, Duration)> for ScheduleAfter {
184    fn from((id, generation, after): (TimerId, TimerGeneration, Duration)) -> Self {
185        Self::new(id, generation, after)
186    }
187}
188
189impl behavior::InterpreterRequest for ScheduleAfter {
190    type ReturnToEmitter = behavior::ReturnsToEmitter<TimerElapsed, behavior::Here>;
191    type LogicalProtocols = behavior::NoBirthProtocols;
192}
193
194impl ActionItem for ScheduleAfter {
195    type Custody = (Option<Self>, Option<Self::Reply>);
196    type Input<'a>
197        = &'a mut Option<Self>
198    where
199        Self: 'a;
200    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
201
202    fn prepare_interpretation(
203        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
204    ) {
205        prepare_item::<Self>(progress);
206    }
207
208    fn interpretation_input<'a>(
209        custody: &'a mut Self::Custody,
210    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
211    where
212        Self: 'a,
213    {
214        match custody {
215            (input @ Some(_), received @ None) => Some((input, received)),
216            _ => None,
217        }
218    }
219
220    fn finish_interpretation(
221        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
222    ) {
223        finish_item::<Self>(progress);
224    }
225
226    type Accepted = TimerScheduled;
227    type Rejection = ScheduleAfterRejection;
228    type Prerequisite = behavior::Never;
229}
230
231impl SourceAction for ScheduleAfter {
232    type Source = Self;
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq)]
236pub struct TimerElapsed {
237    pub id: TimerId,
238    pub generation: TimerGeneration,
239}
240
241impl TimerElapsed {
242    #[must_use]
243    pub const fn new(id: TimerId, generation: TimerGeneration) -> Self {
244        Self { id, generation }
245    }
246}
247
248impl From<(TimerId, TimerGeneration)> for TimerElapsed {
249    fn from((id, generation): (TimerId, TimerGeneration)) -> Self {
250        Self::new(id, generation)
251    }
252}
253
254/// Ask the local interpreter to observe the exact peer incarnation selected at
255/// `peer` when this request is interpreted.
256///
257/// [`PeerStopped`] is the pure result protocol. It arrives eventually if a
258/// selected live incarnation later terminates, or may arrive immediately when
259/// the interpreter has authoritative retained termination for the requested
260/// incarnation. Absence from a live-address table is not such authority: an
261/// interpreter that can select neither a live incarnation nor retained terminal
262/// history must return the complete request with
263/// [`PeerObservationRejection::UnknownAddress`] rather than fabricate a stop
264/// result.
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
266pub struct ObservePeer<A: Address> {
267    pub peer: A,
268}
269
270impl<A: Address> From<A> for ObservePeer<A> {
271    fn from(peer: A) -> Self {
272        Self::new(peer)
273    }
274}
275
276impl<A: Address> ObservePeer<A> {
277    #[must_use]
278    pub const fn new(peer: A) -> Self {
279        Self { peer }
280    }
281}
282
283/// Exact reason a logical peer observation was not established.
284#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
285pub enum PeerObservationRejection {
286    /// No live incarnation or authoritative retained termination exists at the
287    /// requested logical address.
288    #[error("no actor incarnation exists at the requested logical address")]
289    UnknownAddress,
290}
291
292impl<A: Address> behavior::InterpreterRequest for ObservePeer<A> {
293    type ReturnToEmitter = behavior::ReturnsToEmitter<PeerStopped<A>, behavior::Here>;
294    type LogicalProtocols = behavior::NoBirthProtocols;
295}
296
297/// Acceptance establishes the relationship and leaves unit; its later result
298/// is [`PeerStopped`]. Name resolution is the only expected rejection, and the
299/// request has no prerequisite.
300impl<A> ActionItem for ObservePeer<A>
301where
302    A: Address + Send,
303{
304    type Custody = (Option<Self>, Option<Self::Reply>);
305    type Input<'a>
306        = &'a mut Option<Self>
307    where
308        Self: 'a;
309    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
310
311    fn prepare_interpretation(
312        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
313    ) {
314        prepare_item::<Self>(progress);
315    }
316
317    fn interpretation_input<'a>(
318        custody: &'a mut Self::Custody,
319    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
320    where
321        Self: 'a,
322    {
323        match custody {
324            (input @ Some(_), received @ None) => Some((input, received)),
325            _ => None,
326        }
327    }
328
329    fn finish_interpretation(
330        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
331    ) {
332        finish_item::<Self>(progress);
333    }
334
335    type Accepted = ();
336    type Rejection = PeerObservationRejection;
337    type Prerequisite = behavior::Never;
338}
339
340/// Ask the local interpreter to cancel this actor's observation of `peer`.
341///
342/// Peer observation is a derived Bombay protocol, not an actor-model
343/// primitive. The address selects every observer-local definition for that
344/// peer created by [`ObservePeer`]. Exact-incarnation capture and cancellation
345/// belong to the interpreter. Cancellation does not retract a [`PeerStopped`]
346/// event already admitted to the actor's mailbox, and an interpreter treats a
347/// request for relationships that are no longer present as inert. Distinct
348/// structural observations remain independent until this explicit
349/// address-wide cancellation policy is selected.
350#[derive(Debug, Clone, Copy, PartialEq, Eq)]
351pub struct UnwatchPeer<A> {
352    pub peer: A,
353}
354
355impl<A> UnwatchPeer<A> {
356    #[must_use]
357    pub const fn new(peer: A) -> Self {
358        Self { peer }
359    }
360}
361
362impl<A> From<A> for UnwatchPeer<A> {
363    fn from(peer: A) -> Self {
364        Self::new(peer)
365    }
366}
367
368#[derive(Debug, Clone, PartialEq, Eq)]
369pub struct PeerStopped<A: Address> {
370    pub peer: A,
371    pub outcome: Result<Exit<A>, Crash>,
372}
373
374impl<A: Address> PeerStopped<A> {
375    #[must_use]
376    pub fn new(peer: A, outcome: Result<Exit<A>, Crash>) -> Self {
377        Self { peer, outcome }
378    }
379}
380
381impl<A: Address> From<(A, Result<Exit<A>, Crash>)> for PeerStopped<A> {
382    fn from((peer, outcome): (A, Result<Exit<A>, Crash>)) -> Self {
383        Self { peer, outcome }
384    }
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq)]
388pub struct ChildStopped<A: Address> {
389    pub child: CreationId,
390    pub outcome: Result<Exit<A>, Crash>,
391    pub at: Instant,
392}
393
394impl<A: Address> ChildStopped<A> {
395    #[must_use]
396    pub fn new(child: CreationId, outcome: Result<Exit<A>, Crash>, at: Instant) -> Self {
397        Self { child, outcome, at }
398    }
399}
400
401impl<A: Address> From<(CreationId, Result<Exit<A>, Crash>, Instant)> for ChildStopped<A> {
402    fn from((child, outcome, at): (CreationId, Result<Exit<A>, Crash>, Instant)) -> Self {
403        Self { child, outcome, at }
404    }
405}
406
407/// Ask the local interpreter to observe the exact child generation of protocol
408/// `P` bound at one creator-local creation ID.
409///
410/// Creation is resolved before same-action service sends. If that creation was
411/// rejected, the request is blocked by its exact
412/// [`behavior::CreationCorrelation`]. An established child is observed through
413/// its concrete protocol and declared occurrence; equal address types do not
414/// make different child protocols interchangeable.
415///
416/// ```compile_fail,E0308
417/// struct Worker;
418/// impl behavior::Protocol for Worker {
419///     type Addr = behavior::MailAddr;
420///     type Msg = ();
421/// }
422/// struct Account;
423/// impl behavior::Protocol for Account {
424///     type Addr = behavior::MailAddr;
425///     type Msg = ();
426/// }
427/// let child = behavior::CreationSequence::new()
428///     .issue()
429///     .expect("the first creation ID exists");
430/// let account = behavior_actors::ObserveChild::<Account, behavior::ChildHead>::new(child);
431/// let _: behavior_actors::ObserveChild<Worker, behavior::ChildHead> = account;
432/// ```
433pub struct ObserveChild<P: Protocol, Occurrence> {
434    pub child: CreationId,
435    protocol: core::marker::PhantomData<fn() -> P>,
436    occurrence: core::marker::PhantomData<fn() -> Occurrence>,
437}
438
439impl<P: Protocol, Occurrence> Copy for ObserveChild<P, Occurrence> {}
440
441impl<P: Protocol, Occurrence> Clone for ObserveChild<P, Occurrence> {
442    fn clone(&self) -> Self {
443        *self
444    }
445}
446
447impl<P: Protocol, Occurrence> PartialEq for ObserveChild<P, Occurrence> {
448    fn eq(&self, other: &Self) -> bool {
449        self.child == other.child
450    }
451}
452
453impl<P: Protocol, Occurrence> Eq for ObserveChild<P, Occurrence> {}
454
455impl<P: Protocol, Occurrence> core::fmt::Debug for ObserveChild<P, Occurrence> {
456    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
457        formatter
458            .debug_struct("ObserveChild")
459            .field("child", &self.child)
460            .finish()
461    }
462}
463
464impl<P: Protocol, Occurrence> ObserveChild<P, Occurrence> {
465    #[must_use]
466    pub const fn new(child: CreationId) -> Self {
467        Self {
468            child,
469            protocol: core::marker::PhantomData,
470            occurrence: core::marker::PhantomData,
471        }
472    }
473}
474
475impl<P: Protocol, Occurrence> behavior::InterpreterRequest for ObserveChild<P, Occurrence> {
476    type ReturnToEmitter = behavior::ReturnsToEmitter<ChildStopped<P::Addr>, behavior::Here>;
477    type LogicalProtocols = behavior::NoBirthProtocols;
478}
479
480impl<P, Occurrence> behavior::ActionItem for ObserveChild<P, Occurrence>
481where
482    P: Protocol,
483    <P::Addr as Address>::Nonce: Send,
484{
485    type Custody = (Option<Self>, Option<Self::Reply>);
486    type Input<'a>
487        = &'a mut Option<Self>
488    where
489        Self: 'a;
490    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
491
492    fn prepare_interpretation(
493        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
494    ) {
495        prepare_item::<Self>(progress);
496    }
497
498    fn interpretation_input<'a>(
499        custody: &'a mut Self::Custody,
500    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
501    where
502        Self: 'a,
503    {
504        match custody {
505            (input @ Some(_), received @ None) => Some((input, received)),
506            _ => None,
507        }
508    }
509
510    fn finish_interpretation(
511        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
512    ) {
513        finish_item::<Self>(progress);
514    }
515
516    type Accepted = ();
517    type Rejection = behavior::Never;
518    type Prerequisite = behavior::CreationCorrelation<P, Occurrence>;
519}
520
521/// The committed result of one staged [`behavior::CreateChild`] request.
522///
523/// `Installed` is emitted only after fresh allocation, successful
524/// initialization, and binding at `nonce`. The replacement provenance is the
525/// provenance supplied by Behavior; an interpreter must never infer it from
526/// address reuse or creation order.
527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
528pub struct CreationResolved<A: behavior::Address> {
529    pub creation: CreationId,
530    pub kind: CreationKind,
531    pub result: Result<A, CreationRejection>,
532}
533
534impl<A: behavior::Address> CreationResolved<A> {
535    #[must_use]
536    pub const fn new(
537        creation: CreationId,
538        kind: CreationKind,
539        result: Result<A, CreationRejection>,
540    ) -> Self {
541        Self {
542            creation,
543            kind,
544            result,
545        }
546    }
547
548    #[must_use]
549    pub const fn installed(creation: CreationId, kind: CreationKind, address: A) -> Self {
550        Self::new(creation, kind, Ok(address))
551    }
552
553    /// A successfully committed ordinary birth.
554    #[must_use]
555    pub const fn birth(creation: CreationId, address: A) -> Self {
556        Self::installed(creation, CreationKind::Birth, address)
557    }
558
559    /// A successfully committed replacement incarnation.
560    #[must_use]
561    pub const fn replacement(creation: CreationId, previous: CreationId, address: A) -> Self {
562        Self::installed(creation, CreationKind::replacement(previous), address)
563    }
564
565    #[must_use]
566    pub const fn rejected(
567        creation: CreationId,
568        kind: CreationKind,
569        rejection: CreationRejection,
570    ) -> Self {
571        Self::new(creation, kind, Err(rejection))
572    }
573}
574
575impl<A: behavior::Address> From<(CreationId, CreationKind, Result<A, CreationRejection>)>
576    for CreationResolved<A>
577{
578    fn from(
579        (creation, kind, result): (CreationId, CreationKind, Result<A, CreationRejection>),
580    ) -> Self {
581        Self {
582            creation,
583            kind,
584            result,
585        }
586    }
587}
588
589/// Ask the local interpreter to return the committed result of the same-action
590/// creation ID through the behavior's typed creation-result lane.
591///
592/// Equal address and message types do not make child protocols substitutable:
593///
594/// ```compile_fail,E0308
595/// struct Store;
596/// struct Gateway;
597/// impl behavior::Protocol for Store {
598///     type Addr = behavior::MailAddr;
599///     type Msg = ();
600/// }
601/// impl behavior::Protocol for Gateway {
602///     type Addr = behavior::MailAddr;
603///     type Msg = ();
604/// }
605/// let creation = behavior::CreationSequence::new()
606///     .issue()
607///     .expect("the child creation ID exists");
608/// let gateway = behavior_actors::ObserveCreation::<Gateway, behavior::ChildHead>::new(creation);
609/// let _: behavior_actors::ObserveCreation<Store, behavior::ChildHead> = gateway;
610/// ```
611pub struct ObserveCreation<P: Protocol, Occurrence> {
612    pub creation: CreationId,
613    occurrence: core::marker::PhantomData<fn() -> (P, Occurrence)>,
614}
615
616impl<P: Protocol, Occurrence> Copy for ObserveCreation<P, Occurrence> {}
617
618impl<P: Protocol, Occurrence> Clone for ObserveCreation<P, Occurrence> {
619    fn clone(&self) -> Self {
620        *self
621    }
622}
623
624impl<P: Protocol, Occurrence> PartialEq for ObserveCreation<P, Occurrence> {
625    fn eq(&self, other: &Self) -> bool {
626        self.creation == other.creation
627    }
628}
629
630impl<P: Protocol, Occurrence> Eq for ObserveCreation<P, Occurrence> {}
631
632impl<P: Protocol, Occurrence> core::fmt::Debug for ObserveCreation<P, Occurrence> {
633    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
634        formatter
635            .debug_struct("ObserveCreation")
636            .field("creation", &self.creation)
637            .finish()
638    }
639}
640
641impl<P: Protocol, Occurrence> ObserveCreation<P, Occurrence> {
642    #[must_use]
643    pub const fn new(creation: CreationId) -> Self {
644        Self {
645            creation,
646            occurrence: core::marker::PhantomData,
647        }
648    }
649}
650
651impl<P: Protocol, Occurrence> behavior::InterpreterRequest for ObserveCreation<P, Occurrence> {
652    type ReturnToEmitter = behavior::ReturnsToEmitter<CreationResolved<P::Addr>, behavior::Here>;
653    type LogicalProtocols = behavior::NoBirthProtocols;
654}
655
656impl<P, Occurrence> behavior::ActionItem for ObserveCreation<P, Occurrence>
657where
658    P: Protocol,
659    <P::Addr as Address>::Nonce: Send,
660{
661    type Custody = (Option<Self>, Option<Self::Reply>);
662    type Input<'a>
663        = &'a mut Option<Self>
664    where
665        Self: 'a;
666    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
667
668    fn prepare_interpretation(
669        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
670    ) {
671        prepare_item::<Self>(progress);
672    }
673
674    fn interpretation_input<'a>(
675        custody: &'a mut Self::Custody,
676    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
677    where
678        Self: 'a,
679    {
680        match custody {
681            (input @ Some(_), received @ None) => Some((input, received)),
682            _ => None,
683        }
684    }
685
686    fn finish_interpretation(
687        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
688    ) {
689        finish_item::<Self>(progress);
690    }
691
692    type Accepted = ();
693    type Rejection = behavior::Never;
694    type Prerequisite = behavior::CreationCorrelation<P, Occurrence>;
695}
696
697/// A request to finish through one serialized behavior transition.
698#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
699pub struct ShutdownRequested;
700
701/// Ask the local interpreter to begin orderly shutdown of one established
702/// child of protocol `C` in the emitting actor's namespace.
703///
704/// Acceptance is not completion. A successfully accepted request is completed
705/// only by the corresponding [`ChildStopped`] fact. If the interpreter cannot
706/// select an established `C` child, it must return [`ChildShutdownRejected`]
707/// rather than fabricate termination or fail the whole action application.
708/// Protocol identity is retained in the type even when two child protocols use
709/// the same address and nonce types:
710///
711/// ```compile_fail
712/// struct Queue;
713/// struct Worker;
714/// macro_rules! inert {
715///     ($actor:ty) => {
716///         impl behavior::Protocol for $actor {
717///             type Addr = behavior::MailAddr;
718///             type Msg = u8;
719///         }
720///         impl behavior::Behavior for $actor {
721///             type Protocol = Self;
722///             type Event = behavior::User<behavior::MailAddr, u8>;
723///             type Sends = Vec<behavior::Never>;
724///             type Ph = behavior::Never;
725///             type Error = behavior::Never;
726///             type Birth = behavior::NoBirths;
727///             fn init(&mut self, _: behavior::InitializationTurn) -> behavior::BehaviorActed<Self> {
728///                 Ok(behavior::Actions::cont())
729///             }
730///             fn transition(&mut self, _: behavior::ActiveTurn, _: Self::Event) -> behavior::BehaviorActed<Self> {
731///                 Ok(behavior::Actions::cont())
732///             }
733///         }
734///     };
735/// }
736/// inert!(Queue);
737/// inert!(Worker);
738///
739/// let child = behavior::CreationSequence::new()
740///     .issue()
741///     .expect("the first creation ID exists");
742/// let queue = behavior_actors::ShutdownChild::<Queue, behavior::ChildHead>::new(child);
743/// let _: behavior_actors::ShutdownChild<Worker, behavior::ChildHead> = queue;
744/// ```
745///
746/// Repeated occurrences of the same behavior are also incompatible:
747///
748/// ```compile_fail
749/// struct Worker;
750/// impl behavior::Protocol for Worker {
751///     type Addr = behavior::MailAddr;
752///     type Msg = ();
753/// }
754/// impl behavior::Behavior for Worker {
755///     type Protocol = Self;
756///     type Event = behavior::User<behavior::MailAddr, ()>;
757///     type Sends = Vec<behavior::Never>;
758///     type Ph = behavior::Never;
759///     type Error = behavior::Never;
760///     type Birth = behavior::NoBirths;
761///     fn transition(
762///         &mut self,
763///         _: behavior::ActiveTurn,
764///         _: Self::Event,
765///     ) -> behavior::BehaviorActed<Self> {
766///         Ok(behavior::Actions::cont())
767///     }
768/// }
769/// let child = behavior::CreationSequence::new()
770///     .issue()
771///     .expect("the first creation ID exists");
772/// let first = behavior_actors::ShutdownChild::<Worker, behavior::ChildHead>::new(child);
773/// let _: behavior_actors::ShutdownChild<Worker, behavior::ChildTail<behavior::ChildHead>> = first;
774/// ```
775pub struct ShutdownChild<C: behavior::Behavior, Occurrence> {
776    pub child: CreationId,
777    /// Exact shutdown owner in the selected child behavior.
778    pub ingress: behavior::Ingress<ShutdownRequested, behavior::Here>,
779    protocol: core::marker::PhantomData<fn() -> (C, Occurrence)>,
780}
781
782impl<C: behavior::Behavior, Occurrence> ShutdownChild<C, Occurrence> {
783    #[must_use]
784    pub const fn new(child: CreationId) -> Self {
785        Self {
786            child,
787            ingress: behavior::Ingress::new(),
788            protocol: core::marker::PhantomData,
789        }
790    }
791}
792
793impl<C: behavior::Behavior, Occurrence> behavior::InterpreterRequest
794    for ShutdownChild<C, Occurrence>
795{
796    type ReturnToEmitter = behavior::ReturnsToEmitter<ChildShutdownRejected, behavior::Here>;
797    type LogicalProtocols = behavior::NoBirthProtocols;
798}
799
800impl<C, Occurrence> behavior::ActionItem for ShutdownChild<C, Occurrence>
801where
802    C: behavior::Behavior,
803    <behavior::BehaviorAddr<C> as behavior::Address>::Nonce: Send,
804{
805    type Custody = (Option<Self>, Option<Self::Reply>);
806    type Input<'a>
807        = &'a mut Option<Self>
808    where
809        Self: 'a;
810    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
811
812    fn prepare_interpretation(
813        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
814    ) {
815        prepare_item::<Self>(progress);
816    }
817
818    fn interpretation_input<'a>(
819        custody: &'a mut Self::Custody,
820    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
821    where
822        Self: 'a,
823    {
824        match custody {
825            (input @ Some(_), received @ None) => Some((input, received)),
826            _ => None,
827        }
828    }
829
830    fn finish_interpretation(
831        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
832    ) {
833        finish_item::<Self>(progress);
834    }
835
836    type Accepted = ();
837    type Rejection = ChildShutdownRejection;
838    type Prerequisite = behavior::CreationCorrelation<C::Protocol, Occurrence>;
839}
840
841impl<C: behavior::Behavior, Occurrence> Copy for ShutdownChild<C, Occurrence> {}
842
843impl<C: behavior::Behavior, Occurrence> Clone for ShutdownChild<C, Occurrence> {
844    fn clone(&self) -> Self {
845        *self
846    }
847}
848
849impl<C: behavior::Behavior, Occurrence> PartialEq for ShutdownChild<C, Occurrence> {
850    fn eq(&self, other: &Self) -> bool {
851        self.child == other.child
852    }
853}
854
855impl<C: behavior::Behavior, Occurrence> Eq for ShutdownChild<C, Occurrence> {}
856
857impl<C: behavior::Behavior, Occurrence> core::fmt::Debug for ShutdownChild<C, Occurrence> {
858    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
859        f.debug_struct("ShutdownChild")
860            .field("child", &self.child)
861            .finish()
862    }
863}
864
865/// Why a local child-shutdown request was not accepted.
866#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
867pub enum ChildShutdownRejection {
868    /// No established child is bound at the requested creator-local nonce.
869    #[error("no established child exists at the requested nonce")]
870    NotEstablished,
871    /// Shutdown was already accepted for the selected child.
872    #[error("child shutdown is already in progress")]
873    AlreadyStopping,
874}
875
876/// Explicit failed resolution of one [`ShutdownChild`] request.
877#[derive(Debug, Clone, Copy, PartialEq, Eq)]
878pub struct ChildShutdownRejected {
879    pub child: CreationId,
880    pub reason: ChildShutdownRejection,
881}
882
883impl ChildShutdownRejected {
884    #[must_use]
885    pub const fn new(child: CreationId, reason: ChildShutdownRejection) -> Self {
886        Self { child, reason }
887    }
888}
889
890impl From<(CreationId, ChildShutdownRejection)> for ChildShutdownRejected {
891    fn from((child, reason): (CreationId, ChildShutdownRejection)) -> Self {
892        Self { child, reason }
893    }
894}
895
896#[cfg(test)]
897mod tests {
898    use super::*;
899    use behavior::MailAddr;
900
901    fn creation(number: u64) -> CreationId {
902        let mut sequence = behavior::CreationSequence::new();
903        (0..number)
904            .filter_map(|_| sequence.issue())
905            .last()
906            .unwrap_or_else(|| panic!("test creation ID is issued"))
907    }
908
909    #[test]
910    fn one_creation_correlates_observation_creation_and_shutdown() {
911        enum WorkerRole {}
912
913        struct Worker;
914
915        impl behavior::Protocol for Worker {
916            type Addr = MailAddr;
917            type Msg = behavior::Never;
918        }
919
920        impl behavior::Behavior for Worker {
921            type Protocol = Self;
922            type Event = behavior::User<MailAddr, behavior::Never>;
923            type Sends = Vec<behavior::Never>;
924            type Ph = behavior::Never;
925            type Error = behavior::Never;
926            type Birth = behavior::NoBirths;
927
928            fn transition(
929                &mut self,
930                _: behavior::ActiveTurn,
931                event: Self::Event,
932            ) -> behavior::BehaviorActed<Self> {
933                match event.message {}
934            }
935        }
936
937        let child = creation(13);
938
939        fn copy_without_occurrence_bounds<T: Copy>() {}
940        copy_without_occurrence_bounds::<ObserveChild<Worker, WorkerRole>>();
941        copy_without_occurrence_bounds::<ObserveCreation<Worker, WorkerRole>>();
942        copy_without_occurrence_bounds::<ShutdownChild<Worker, WorkerRole>>();
943
944        assert_eq!(ObserveChild::<Worker, WorkerRole>::new(child).child, child);
945        assert_eq!(
946            ObserveCreation::<Worker, WorkerRole>::new(child).creation,
947            child
948        );
949        assert_eq!(ShutdownChild::<Worker, WorkerRole>::new(child).child, child);
950    }
951
952    #[test]
953    fn expected_lane_types_infer_lossless_protocol_products() {
954        let peer: ObservePeer<MailAddr> = MailAddr(7).into();
955        let child: ObserveChild<
956            behavior::MessageProtocol<MailAddr, behavior::Never>,
957            behavior::ChildHead,
958        > = ObserveChild::new(creation(9));
959        let observed_creation: ObserveCreation<
960            behavior::MessageProtocol<MailAddr, behavior::Never>,
961            behavior::ChildHead,
962        > = ObserveCreation::new(creation(11));
963        let rejected: ChildShutdownRejected =
964            (creation(13), ChildShutdownRejection::NotEstablished).into();
965
966        assert_eq!(peer.peer, MailAddr(7));
967        assert_eq!(child.child.get(), 9);
968        assert_eq!(observed_creation.creation.get(), 11);
969        assert_eq!(rejected.child.get(), 13);
970    }
971
972    #[test]
973    fn timer_newtypes_and_requests_have_lossless_construction() {
974        let id = TimerId::from(2);
975        let generation = TimerGeneration::from(5);
976        assert_eq!(u64::from(id), 2);
977        assert_eq!(u64::from(generation), 5);
978        assert_eq!(
979            TimerElapsed::from((id, generation)),
980            TimerElapsed::new(id, generation)
981        );
982    }
983}