Skip to main content

behavior/actor/
creation.rs

1//! Staged fresh-actor creation capabilities.
2
3use core::future::Future;
4use core::marker::PhantomData;
5
6use super::addressing::{Address, ChildRecipient};
7use crate::next::Never;
8use crate::{Behavior, Protocol};
9
10/// Behavior-owned provenance for a staged fresh actor creation request.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum CreationKind<N> {
13    /// An initial or ordinary later birth.
14    Birth,
15    /// A fresh successor incarnation requested by a replacement protocol.
16    ReplacementIncarnation {
17        /// The exact child incarnation this fresh actor is intended to replace.
18        replaces: N,
19    },
20}
21
22impl<N> CreationKind<N> {
23    #[must_use]
24    pub const fn replacement_of(replaces: N) -> Self {
25        Self::ReplacementIncarnation { replaces }
26    }
27}
28
29/// A staged request to establish a fresh child at a creator-local nonce.
30///
31/// The nonce is a routing and correlation key, not an actor identity or proof
32/// of freshness. The kind is Behavior-owned intent; a runtime's typed creation
33/// resolution is the corresponding committed fact. Replacement at an existing
34/// address is deliberately absent; stable identity is derived with a proxy
35/// actor.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Create<A: Address, New> {
38    pub nonce: A::Nonce,
39    pub child: New,
40    pub kind: CreationKind<A::Nonce>,
41}
42
43impl<A: Address, New> Create<A, New> {
44    #[must_use]
45    pub const fn new(nonce: A::Nonce, child: New, kind: CreationKind<A::Nonce>) -> Self {
46        Self { nonce, child, kind }
47    }
48
49    #[must_use]
50    pub const fn birth(nonce: A::Nonce, child: New) -> Self {
51        Self::new(nonce, child, CreationKind::Birth)
52    }
53
54    #[must_use]
55    pub const fn replacement_incarnation(nonce: A::Nonce, replaces: A::Nonce, child: New) -> Self {
56        Self::new(nonce, child, CreationKind::replacement_of(replaces))
57    }
58}
59
60/// A named creator-local route for one statically declared child role.
61///
62/// The route carries routing intent only. Its nonce is not an actor identity,
63/// proof of freshness, or evidence that creation succeeded. `Child` fixes the
64/// exact behavior accepted by [`ChildRoute::birth`], while `Role` keeps two
65/// declarations of the same child behavior nominally distinct.
66pub struct ChildRoute<Child: Behavior, Role> {
67    recipient: ChildRecipient<Child::Protocol>,
68    child: PhantomData<fn() -> Child>,
69    role: PhantomData<fn() -> Role>,
70}
71
72/// Static proof that `Role` names one exact direct child of `Parent`.
73///
74/// Behavior authoring owns this relationship. A runtime may use the proof to
75/// build application topology, but the role itself allocates nothing and is
76/// not evidence that the child was created or installed.
77pub trait ChildRole<Parent: Behavior> {
78    /// The only child behavior accepted at this role.
79    type Child: Behavior;
80
81    /// Structural position of this role in `Parent`'s closed child sum.
82    type Position: ChildPosition<<Parent::Birth as BirthMode>::Child, Self::Child>;
83}
84
85impl<Child: Behavior, Role> Copy for ChildRoute<Child, Role> {}
86
87impl<Child: Behavior, Role> Clone for ChildRoute<Child, Role> {
88    fn clone(&self) -> Self {
89        *self
90    }
91}
92
93impl<Child: Behavior, Role> PartialEq for ChildRoute<Child, Role> {
94    fn eq(&self, other: &Self) -> bool {
95        self.recipient == other.recipient
96    }
97}
98
99impl<Child: Behavior, Role> Eq for ChildRoute<Child, Role> {}
100
101impl<Child: Behavior, Role> core::fmt::Debug for ChildRoute<Child, Role>
102where
103    <<Child::Protocol as Protocol>::Addr as Address>::Nonce: core::fmt::Debug,
104{
105    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106        f.debug_struct("ChildRoute")
107            .field("nonce", &self.nonce())
108            .finish()
109    }
110}
111
112impl<Child: Behavior, Role> ChildRoute<Child, Role> {
113    /// Name this declared role at a creator-local route.
114    #[must_use]
115    pub const fn new(nonce: <<Child::Protocol as Protocol>::Addr as Address>::Nonce) -> Self {
116        Self {
117            recipient: ChildRecipient::new(nonce),
118            child: PhantomData,
119            role: PhantomData,
120        }
121    }
122
123    /// Return the creator-local recipient for this declared child role.
124    #[must_use]
125    pub const fn recipient(self) -> ChildRecipient<Child::Protocol> {
126        self.recipient
127    }
128
129    /// Return the creator-local nonce used by creation and lifecycle facts.
130    #[must_use]
131    pub const fn nonce(self) -> <<Child::Protocol as Protocol>::Addr as Address>::Nonce {
132        self.recipient.nonce()
133    }
134
135    /// Stage one creation request with explicit Behavior-owned provenance.
136    #[must_use]
137    pub const fn stage(
138        self,
139        child: Child,
140        kind: CreationKind<<<Child::Protocol as Protocol>::Addr as Address>::Nonce>,
141    ) -> Create<<Child::Protocol as Protocol>::Addr, Child> {
142        Create::new(self.nonce(), child, kind)
143    }
144
145    /// Stage an ordinary fresh-birth request for this declared child role.
146    #[must_use]
147    pub const fn birth(self, child: Child) -> Create<<Child::Protocol as Protocol>::Addr, Child> {
148        self.stage(child, CreationKind::Birth)
149    }
150
151    /// Stage a replacement incarnation for this declared child role.
152    #[must_use]
153    pub const fn replacement_incarnation(
154        self,
155        replaces: <<Child::Protocol as Protocol>::Addr as Address>::Nonce,
156        child: Child,
157    ) -> Create<<Child::Protocol as Protocol>::Addr, Child> {
158        self.stage(child, CreationKind::replacement_of(replaces))
159    }
160}
161
162/// Static interpreter capability for installing one concrete child behavior.
163///
164/// An interpreter implements this trait separately for every concrete child
165/// behavior it can install. `C::Protocol` remains the canonical hosting
166/// identity at this boundary: distinct child behaviors may select the same
167/// protocol and therefore share one runtime-owned protocol space while still
168/// receiving distinct installation calls. Semantic child roles and routes are
169/// deliberately not storage identities. Heterogeneous child sums require all
170/// applicable implementations through recursive static bounds, so unsupported
171/// alternatives fail to compile instead of falling through to a registry or
172/// erased path. The returned future is sendable so an interpreter may remain
173/// inside a thread-safe recursive driver future.
174pub trait InstallBirth<A: Address, C: Behavior, Output, Error>
175where
176    C::Protocol: Protocol<Addr = A>,
177{
178    /// Install and commit exactly the supplied concrete creation.
179    ///
180    /// # Errors
181    /// Returns the interpreter's typed allocation, initialization, or commit
182    /// failure without binding the requested nonce.
183    fn install_birth(
184        &mut self,
185        creation: Create<A, C>,
186    ) -> impl Future<Output = Result<Output, Error>> + Send;
187}
188
189/// Exhaustive static dispatch of one creation-only child sum.
190///
191/// This is an interpreter-facing derived construction, not another actor-model
192/// operation. Implementations must preserve the nonce and provenance and call
193/// exactly one concrete [`InstallBirth`] implementation. [`ChildChoice`]
194/// provides the closed recursive heterogeneous sum. Dispatch futures are
195/// sendable; heterogeneous sums therefore require sendable alternatives,
196/// creator-local nonces, and installers.
197pub trait DispatchBirth<A: Address, Installer, Output, Error>: Sized {
198    /// Select exactly one concrete installer while preserving creation data.
199    ///
200    /// # Errors
201    /// Returns the selected concrete installer's typed failure unchanged.
202    fn dispatch_birth(
203        self,
204        nonce: A::Nonce,
205        kind: CreationKind<A::Nonce>,
206        installer: &mut Installer,
207    ) -> impl Future<Output = Result<Output, Error>> + Send;
208}
209
210/// One alternative in a closed, recursively composed child-creation sum.
211///
212/// `Head` is one concrete child behavior and `Tail` is the remaining closed
213/// sum. This is a creation choice only: it is not a behavior, message
214/// envelope, registry, or runtime dispatch mechanism.
215///
216/// Every alternative requires a concrete installer; incomplete interpreter
217/// support is rejected statically:
218///
219/// ```compile_fail
220/// use behavior::{
221///     Actions, Behavior, BehaviorActed, ChildChoice, Create, DispatchBirth,
222///     InstallBirth, MailAddr, Never, NoBirths, Protocol, User,
223/// };
224///
225/// struct First;
226/// struct Second;
227///
228/// macro_rules! inert {
229///     ($child:ty) => {
230///         impl Protocol for $child {
231///             type Addr = MailAddr;
232///             type Msg = Never;
233///         }
234///         impl Behavior for $child {
235///             type Event = User<MailAddr, Never>;
236///             type Sends = Vec<Never>;
237///             type Ph = Never;
238///             type Error = Never;
239///             type Birth = NoBirths;
240///
241///             fn transition(
242///                 &mut self,
243///                 _: behavior::ActiveTurn,
244///                 event: Self::Event,
245///             ) -> BehaviorActed<Self> {
246///                 match event.message {}
247///             }
248///         }
249///     };
250/// }
251/// inert!(First);
252/// inert!(Second);
253///
254/// struct Incomplete;
255/// impl InstallBirth<MailAddr, First, (), Never> for Incomplete {
256///     async fn install_birth(
257///         &mut self,
258///         _: Create<MailAddr, First>,
259///     ) -> Result<(), Never> {
260///         Ok(())
261///     }
262/// }
263///
264/// type Both = ChildChoice<First, ChildChoice<Second, Never>>;
265/// fn require_complete<T: DispatchBirth<MailAddr, Incomplete, (), Never>>() {}
266/// require_complete::<Both>();
267/// ```
268#[derive(Debug, Clone, PartialEq, Eq)]
269pub enum ChildChoice<Head, Tail> {
270    /// Select the concrete child at this position.
271    Head(Head),
272    /// Select one concrete child from the remaining alternatives.
273    Tail(Tail),
274}
275
276/// Position selecting the head of a closed [`ChildChoice`] sum.
277#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
278pub struct ChildHead;
279
280/// Position selecting inside the tail of a closed [`ChildChoice`] sum.
281#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
282pub struct ChildTail<Position>(PhantomData<fn() -> Position>);
283
284/// Static proof that `Child` occupies `Position` in a closed child sum.
285///
286/// This trait provides structural evidence only. It does not construct a
287/// choice, perform creation, or select a child through runtime inspection.
288/// A role cannot claim a position occupied by a different child:
289///
290/// ```compile_fail
291/// use behavior::{
292///     Actions, Behavior, BehaviorActed, Births, ChildChoice, ChildHead,
293///     ChildRole, MailAddr, Never, NoBirths, User,
294/// };
295/// struct First;
296/// struct Second;
297/// struct Parent;
298/// macro_rules! inert {
299///     ($actor:ty) => {
300///         impl behavior::Protocol for $actor {
301///             type Addr = MailAddr;
302///             type Msg = Never;
303///         }
304///         impl Behavior for $actor {
305///             type Protocol = Self;
306///             type Event = User<MailAddr, Never>;
307///             type Sends = Vec<Never>;
308///             type Ph = Never;
309///             type Error = Never;
310///             type Birth = NoBirths;
311///             fn transition(&mut self, _: behavior::ActiveTurn, event: Self::Event) -> BehaviorActed<Self> {
312///                 match event.message {}
313///             }
314///         }
315///     };
316/// }
317/// inert!(First);
318/// inert!(Second);
319/// impl behavior::Protocol for Parent {
320///     type Addr = MailAddr;
321///     type Msg = Never;
322/// }
323/// impl Behavior for Parent {
324///     type Protocol = Self;
325///     type Event = User<MailAddr, Never>;
326///     type Sends = Vec<Never>;
327///     type Ph = Never;
328///     type Error = Never;
329///     type Birth = Births<ChildChoice<Second, ChildChoice<First, Never>>>;
330///     fn transition(&mut self, _: behavior::ActiveTurn, event: Self::Event) -> BehaviorActed<Self> {
331///         match event.message {}
332///     }
333/// }
334/// struct ForgedFirst;
335/// impl ChildRole<Parent> for ForgedFirst {
336///     type Child = First;
337///     type Position = ChildHead;
338/// }
339/// ```
340pub trait ChildPosition<Children, Child: Behavior>: sealed::ChildPosition {}
341
342impl sealed::ChildPosition for ChildHead {}
343
344impl<Head: Behavior, Tail> ChildPosition<ChildChoice<Head, Tail>, Head> for ChildHead {}
345
346impl<Position> sealed::ChildPosition for ChildTail<Position> {}
347
348impl<Head, Tail, Position, Child> ChildPosition<ChildChoice<Head, Tail>, Child>
349    for ChildTail<Position>
350where
351    Child: Behavior,
352    Position: ChildPosition<Tail, Child>,
353{
354}
355
356mod sealed {
357    pub trait ChildPosition {}
358    pub trait ChildProduct {}
359}
360
361/// The empty heterogeneous creation product.
362#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
363pub struct NoChildren;
364
365impl sealed::ChildProduct for NoChildren {}
366
367/// One creation appended to an ordered heterogeneous child product.
368pub struct ChildCons<A: Address, C, Earlier> {
369    creation: Create<A, C>,
370    earlier: Earlier,
371}
372
373impl<A: Address, C, Earlier> sealed::ChildProduct for ChildCons<A, C, Earlier> {}
374
375/// A pure, ordered heterogeneous product of staged direct-child creations.
376///
377/// This value owns no mailbox, address, runtime actor, or lifecycle. Explicit
378/// creator-local nonces remain caller policy. Conversion rejects duplicates
379/// within this product before producing any `Actions` value; the interpreter
380/// must still reject collisions with already established children.
381pub struct Children<A: Address, Product = NoChildren> {
382    product: Product,
383    address: PhantomData<fn() -> A>,
384}
385
386/// Rejection while converting a heterogeneous child product into creations.
387#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
388pub enum ChildrenError<N> {
389    /// Two declarations selected the same creator-local routing nonce.
390    #[error("duplicate creator-local child nonce")]
391    DuplicateNonce {
392        /// The colliding nonce; no creation product was returned.
393        nonce: N,
394    },
395}
396
397/// Closed recursive conversion implemented only by Bombay child products.
398pub trait ChildProduct<A: Address>: sealed::ChildProduct + Sized {
399    /// Closed sum containing exactly the concrete child behavior types.
400    type Choice;
401
402    #[doc(hidden)]
403    fn stage(
404        self,
405    ) -> Result<(Vec<Create<A, Self::Choice>>, Vec<A::Nonce>), ChildrenError<A::Nonce>>;
406}
407
408impl<A: Address> ChildProduct<A> for NoChildren {
409    type Choice = Never;
410
411    fn stage(
412        self,
413    ) -> Result<(Vec<Create<A, Self::Choice>>, Vec<A::Nonce>), ChildrenError<A::Nonce>> {
414        Ok((Vec::new(), Vec::new()))
415    }
416}
417
418impl<A, C, Earlier> ChildProduct<A> for ChildCons<A, C, Earlier>
419where
420    A: Address,
421    C: Behavior,
422    C::Protocol: Protocol<Addr = A>,
423    Earlier: ChildProduct<A>,
424{
425    type Choice = ChildChoice<C, Earlier::Choice>;
426
427    fn stage(
428        self,
429    ) -> Result<(Vec<Create<A, Self::Choice>>, Vec<A::Nonce>), ChildrenError<A::Nonce>> {
430        let (earlier, mut nonces) = self.earlier.stage()?;
431        if nonces.contains(&self.creation.nonce) {
432            return Err(ChildrenError::DuplicateNonce {
433                nonce: self.creation.nonce,
434            });
435        }
436        let mut creates = earlier
437            .into_iter()
438            .map(|creation| {
439                Create::new(
440                    creation.nonce,
441                    ChildChoice::Tail(creation.child),
442                    creation.kind,
443                )
444            })
445            .collect::<Vec<_>>();
446        nonces.push(self.creation.nonce);
447        creates.push(Create::new(
448            self.creation.nonce,
449            ChildChoice::Head(self.creation.child),
450            self.creation.kind,
451        ));
452        Ok((creates, nonces))
453    }
454}
455
456impl<A: Address> Children<A, NoChildren> {
457    /// Start an empty heterogeneous creation product.
458    #[must_use]
459    pub const fn new() -> Self {
460        Self {
461            product: NoChildren,
462            address: PhantomData,
463        }
464    }
465}
466
467impl<A: Address> Default for Children<A, NoChildren> {
468    fn default() -> Self {
469        Self::new()
470    }
471}
472
473impl<A: Address, Product> Children<A, Product> {
474    /// Append one complete staged creation, preserving its provenance.
475    #[must_use]
476    pub fn create<C>(self, creation: Create<A, C>) -> Children<A, ChildCons<A, C, Product>>
477    where
478        C: Behavior,
479        C::Protocol: Protocol<Addr = A>,
480    {
481        Children {
482            product: ChildCons {
483                creation,
484                earlier: self.product,
485            },
486            address: PhantomData,
487        }
488    }
489
490    /// Append one ordinary fresh-birth request.
491    #[must_use]
492    pub fn child<C>(self, nonce: A::Nonce, child: C) -> Children<A, ChildCons<A, C, Product>>
493    where
494        C: Behavior,
495        C::Protocol: Protocol<Addr = A>,
496    {
497        self.create(Create::birth(nonce, child))
498    }
499
500    /// Append an ordinary birth through one named child-role binding.
501    #[must_use]
502    pub fn child_at<C, Role>(
503        self,
504        route: ChildRoute<C, Role>,
505        child: C,
506    ) -> Children<A, ChildCons<A, C, Product>>
507    where
508        C: Behavior,
509        C::Protocol: Protocol<Addr = A>,
510    {
511        self.create(route.birth(child))
512    }
513}
514
515impl<A, Product> Children<A, Product>
516where
517    A: Address,
518    Product: ChildProduct<A>,
519{
520    /// Validate the shared nonce domain and produce ordered creation requests.
521    ///
522    /// # Errors
523    /// Returns [`ChildrenError::DuplicateNonce`] without returning a partial
524    /// creation vector when any two declarations in this product share a
525    /// nonce. Existing child-namespace collisions remain interpreter-owned
526    /// creation rejections.
527    pub fn into_creates(self) -> Result<Vec<Create<A, Product::Choice>>, ChildrenError<A::Nonce>> {
528        self.product.stage().map(|(creates, _)| creates)
529    }
530}
531
532impl<A, Head, Tail, Installer, Output, Error> DispatchBirth<A, Installer, Output, Error>
533    for ChildChoice<Head, Tail>
534where
535    A: Address,
536    A::Nonce: Send,
537    Head: Behavior + Send,
538    Head::Protocol: Protocol<Addr = A>,
539    Tail: DispatchBirth<A, Installer, Output, Error> + Send,
540    Installer: InstallBirth<A, Head, Output, Error> + Send,
541{
542    async fn dispatch_birth(
543        self,
544        nonce: A::Nonce,
545        kind: CreationKind<A::Nonce>,
546        installer: &mut Installer,
547    ) -> Result<Output, Error> {
548        match self {
549            Self::Head(child) => {
550                installer
551                    .install_birth(Create::new(nonce, child, kind))
552                    .await
553            }
554            Self::Tail(tail) => tail.dispatch_birth(nonce, kind, installer).await,
555        }
556    }
557}
558
559impl<A, Installer, Output, Error> DispatchBirth<A, Installer, Output, Error> for Never
560where
561    A: Address,
562{
563    fn dispatch_birth(
564        self,
565        _nonce: A::Nonce,
566        _kind: CreationKind<A::Nonce>,
567        _installer: &mut Installer,
568    ) -> impl Future<Output = Result<Output, Error>> + Send {
569        async move { match self {} }
570    }
571}
572
573impl<A, C, Installer, Output, Error> DispatchBirth<A, Installer, Output, Error> for C
574where
575    A: Address,
576    C: Behavior,
577    C::Protocol: Protocol<Addr = A>,
578    Installer: InstallBirth<A, C, Output, Error>,
579{
580    fn dispatch_birth(
581        self,
582        nonce: A::Nonce,
583        kind: CreationKind<A::Nonce>,
584        installer: &mut Installer,
585    ) -> impl Future<Output = Result<Output, Error>> + Send {
586        installer.install_birth(Create::new(nonce, self, kind))
587    }
588}
589
590/// A type-level description of a behavior's creation capability.
591pub trait BirthMode {
592    type Child;
593}
594
595/// Empty protocol projection of a closed behavior-birth algebra.
596pub struct NoBirthProtocols;
597
598/// One behavior protocol followed by the remaining closed birth projection.
599pub struct BirthProtocol<P: Protocol, Tail> {
600    protocol: PhantomData<fn() -> P>,
601    tail: PhantomData<fn() -> Tail>,
602}
603
604/// Structural position selecting the current projected birth protocol.
605pub struct BirthProtocolHead;
606
607/// Structural position selecting inside the remaining protocol projection.
608pub struct BirthProtocolTail<Position>(PhantomData<fn() -> Position>);
609
610/// Static membership evidence for one occurrence in a birth-protocol product.
611pub trait BirthProtocolAt<P: Protocol, Position> {}
612
613impl<P: Protocol, Tail> BirthProtocolAt<P, BirthProtocolHead> for BirthProtocol<P, Tail> {}
614
615impl<Head, Tail, P, Position> BirthProtocolAt<P, BirthProtocolTail<Position>>
616    for BirthProtocol<Head, Tail>
617where
618    Head: Protocol,
619    P: Protocol,
620    Tail: BirthProtocolAt<P, Position>,
621{
622}
623
624/// Closed product operation used by the structural birth projection.
625#[doc(hidden)]
626pub trait BirthProtocolProduct {
627    type Append<Tail: BirthProtocolProduct>: BirthProtocolProduct;
628}
629
630impl BirthProtocolProduct for NoBirthProtocols {
631    type Append<Tail: BirthProtocolProduct> = Tail;
632}
633
634impl<P, Rest> BirthProtocolProduct for BirthProtocol<P, Rest>
635where
636    P: Protocol,
637    Rest: BirthProtocolProduct,
638{
639    type Append<Tail: BirthProtocolProduct> = BirthProtocol<P, Rest::Append<Tail>>;
640}
641
642/// Closed static projection of a behavior's own protocol and every protocol
643/// reachable through its transitive staged-birth algebra.
644///
645/// This is structural information derived from [`Behavior::Birth`]. It makes
646/// no hosting or allocation decision and does not inspect send destinations.
647pub trait BirthProtocols: Behavior {
648    type Protocols: BirthProtocolProduct;
649}
650
651impl<B> BirthProtocols for B
652where
653    B: Behavior,
654    B::Birth: BirthModeProtocols,
655{
656    type Protocols = BirthProtocol<B::Protocol, <B::Birth as BirthModeProtocols>::Protocols>;
657}
658
659#[doc(hidden)]
660pub trait BirthModeProtocols {
661    type Protocols: BirthProtocolProduct;
662}
663
664impl<M> BirthModeProtocols for M
665where
666    M: BirthMode,
667    M::Child: BirthNodeProtocols,
668{
669    type Protocols = <M::Child as BirthNodeProtocols>::Protocols;
670}
671
672#[doc(hidden)]
673pub trait BirthNodeProtocols {
674    type Protocols: BirthProtocolProduct;
675}
676
677impl<B> BirthNodeProtocols for B
678where
679    B: BirthProtocols,
680{
681    type Protocols = B::Protocols;
682}
683
684impl<Head, Tail> BirthNodeProtocols for ChildChoice<Head, Tail>
685where
686    Head: BirthNodeProtocols,
687    Tail: BirthNodeProtocols,
688{
689    type Protocols = <Head::Protocols as BirthProtocolProduct>::Append<Tail::Protocols>;
690}
691
692impl BirthNodeProtocols for Never {
693    type Protocols = NoBirthProtocols;
694}
695
696/// This behavior cannot emit child births.
697#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
698pub struct NoBirths;
699
700impl BirthMode for NoBirths {
701    type Child = Never;
702}
703
704/// This behavior may emit births of `C`.
705#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
706pub struct Births<C>(PhantomData<fn() -> C>);
707
708impl<C> BirthMode for Births<C> {
709    type Child = C;
710}
711
712#[cfg(test)]
713mod tests {
714    use super::*;
715    use crate::{Actions, BehaviorActed, MailAddr, NoBirths, User};
716
717    struct Child;
718
719    struct SharedProtocol;
720
721    impl behavior::Protocol for SharedProtocol {
722        type Addr = MailAddr;
723        type Msg = u8;
724    }
725
726    struct Primary;
727    struct Fallback;
728
729    macro_rules! shared_protocol_behavior {
730        ($behavior:ty, $birth:ty) => {
731            impl Behavior for $behavior {
732                type Protocol = SharedProtocol;
733                type Event = User<MailAddr, u8>;
734                type Sends = Vec<Never>;
735                type Ph = Never;
736                type Error = Never;
737                type Birth = $birth;
738
739                fn transition(
740                    &mut self,
741                    _: crate::ActiveTurn,
742                    _: Self::Event,
743                ) -> BehaviorActed<Self> {
744                    Ok(Actions::cont())
745                }
746            }
747        };
748    }
749
750    shared_protocol_behavior!(Primary, Births<Child>);
751    shared_protocol_behavior!(Fallback, NoBirths);
752
753    impl behavior::Protocol for Child {
754        type Addr = MailAddr;
755        type Msg = u8;
756    }
757
758    impl Behavior for Child {
759        type Protocol = Self;
760        type Event = User<MailAddr, u8>;
761        type Sends = Vec<Never>;
762        type Ph = Never;
763        type Error = Never;
764        type Birth = NoBirths;
765
766        fn transition(&mut self, _: crate::ActiveTurn, _: Self::Event) -> BehaviorActed<Self> {
767            Ok(Actions::cont())
768        }
769    }
770
771    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
772    enum InstallError {
773        Refused,
774    }
775
776    struct RecordingInstaller {
777        calls: usize,
778        observed: Vec<(u64, CreationKind<u64>)>,
779        result: Result<u32, InstallError>,
780    }
781
782    #[derive(Debug, PartialEq, Eq)]
783    enum SharedInstallation {
784        Primary(u64),
785        Fallback(u64),
786    }
787
788    #[derive(Default)]
789    struct SharedProtocolInstaller(Vec<SharedInstallation>);
790
791    impl InstallBirth<MailAddr, Primary, (), Never> for SharedProtocolInstaller {
792        async fn install_birth(
793            &mut self,
794            creation: Create<MailAddr, Primary>,
795        ) -> Result<(), Never> {
796            self.0.push(SharedInstallation::Primary(creation.nonce));
797            Ok(())
798        }
799    }
800
801    impl InstallBirth<MailAddr, Fallback, (), Never> for SharedProtocolInstaller {
802        async fn install_birth(
803            &mut self,
804            creation: Create<MailAddr, Fallback>,
805        ) -> Result<(), Never> {
806            self.0.push(SharedInstallation::Fallback(creation.nonce));
807            Ok(())
808        }
809    }
810
811    impl InstallBirth<MailAddr, Child, u32, InstallError> for RecordingInstaller {
812        async fn install_birth(
813            &mut self,
814            creation: Create<MailAddr, Child>,
815        ) -> Result<u32, InstallError> {
816            self.calls += 1;
817            self.observed.push((creation.nonce, creation.kind));
818            self.result
819        }
820    }
821
822    fn installer(result: Result<u32, InstallError>) -> RecordingInstaller {
823        RecordingInstaller {
824            calls: 0,
825            observed: Vec::new(),
826            result,
827        }
828    }
829
830    fn assert_send<T: Send>(_: &T) {}
831
832    enum WorkerRole {}
833
834    #[test]
835    fn child_route_preserves_one_nonce_across_routing_and_creation_provenance() {
836        let route = ChildRoute::<Child, WorkerRole>::new(17);
837
838        assert_eq!(route.nonce(), 17);
839        assert_eq!(route.recipient().nonce(), 17);
840        assert_eq!(route, ChildRoute::new(17));
841        assert_ne!(route, ChildRoute::new(19));
842        assert_eq!(format!("{route:?}"), "ChildRoute { nonce: 17 }");
843
844        let birth = route.birth(Child);
845        assert_eq!(birth.nonce, 17);
846        assert_eq!(birth.kind, CreationKind::Birth);
847
848        let replacement = route.replacement_incarnation(11, Child);
849        assert_eq!(replacement.nonce, 17);
850        assert_eq!(replacement.kind, CreationKind::replacement_of(11));
851    }
852
853    #[test]
854    fn empty_child_product_stages_no_creations_or_nonces() {
855        let (creations, nonces) = <NoChildren as ChildProduct<MailAddr>>::stage(NoChildren)
856            .expect("an empty child product is valid");
857        assert!(creations.is_empty());
858        assert!(nonces.is_empty());
859    }
860
861    #[tokio::test]
862    async fn concrete_child_dispatches_once_with_exact_nonce_provenance_and_output() {
863        let mut installer = installer(Ok(91));
864        let future = Child.dispatch_birth(
865            17,
866            CreationKind::ReplacementIncarnation { replaces: 8 },
867            &mut installer,
868        );
869        assert_send(&future);
870        let result = future.await;
871
872        assert_eq!(result, Ok(91));
873        assert_eq!(installer.calls, 1);
874        assert_eq!(
875            installer.observed,
876            [(17, CreationKind::ReplacementIncarnation { replaces: 8 })]
877        );
878    }
879
880    #[tokio::test]
881    async fn concrete_child_returns_the_exact_installer_error_without_retry() {
882        let mut installer = installer(Err(InstallError::Refused));
883        let future = Child.dispatch_birth(23, CreationKind::replacement_of(19), &mut installer);
884        assert_send(&future);
885        let result = future.await;
886
887        assert_eq!(result, Err(InstallError::Refused));
888        assert_eq!(installer.calls, 1);
889        assert_eq!(installer.observed, [(23, CreationKind::replacement_of(19))]);
890    }
891
892    #[tokio::test]
893    async fn distinct_child_alternatives_preserve_one_canonical_protocol_identity() {
894        type Alternatives = ChildChoice<Primary, ChildChoice<Fallback, Never>>;
895
896        fn requires_shared_protocol<C: Behavior<Protocol = SharedProtocol>>() {}
897        requires_shared_protocol::<Primary>();
898        requires_shared_protocol::<Fallback>();
899
900        let mut installer = SharedProtocolInstaller::default();
901        Alternatives::Head(Primary)
902            .dispatch_birth(11, CreationKind::Birth, &mut installer)
903            .await
904            .unwrap();
905        Alternatives::Tail(ChildChoice::Head(Fallback))
906            .dispatch_birth(17, CreationKind::Birth, &mut installer)
907            .await
908            .unwrap();
909
910        assert_eq!(
911            installer.0,
912            [
913                SharedInstallation::Primary(11),
914                SharedInstallation::Fallback(17),
915            ]
916        );
917    }
918
919    #[test]
920    fn birth_protocol_projection_recurses_without_inspecting_send_lanes() {
921        type Protocols = <Primary as BirthProtocols>::Protocols;
922        type Expected = BirthProtocol<SharedProtocol, BirthProtocol<Child, NoBirthProtocols>>;
923
924        trait Same<T> {}
925        impl<T> Same<T> for T {}
926        fn exact<T: Same<Expected>>() {}
927        exact::<Protocols>();
928    }
929}