Skip to main content

behavior/effects/
actions.rs

1//! The explicit result of one actor behavior transition.
2
3use core::future::{self, Future};
4use core::pin::pin;
5use core::task::Poll;
6
7use super::sending::{
8    ClassifySettlement, InterpretItem, InterpretSends, Interpretation, InterpretationProgress,
9    InterpreterFault, ItemSettlement, SendEffects, SendInput, SendSettlements, SettledItem,
10    SettlementStatus, SourceAdmission, SourceCustody, SourceProgress, SourceSettlementCustody,
11    offer_source_in_order,
12};
13use crate::actor::{
14    Address, BirthMode, Births, ChildCreationProduct, ChildHead, ChildNamespaceExhausted,
15    CreateChild, CreationRejection, Creations, DispatchBirth, NoBirths, RetirementBirths,
16    RoutedCreation,
17};
18use crate::next::{Never, Step, Stopped};
19use crate::transition::{Behavior, BehaviorAddr};
20use crate::user_event::{EventIngress, User, UserEvent};
21
22pub type Become<Ph = Never> = Step<Ph, Stopped>;
23
24/// Complete owned settlement of one [`Actions`] value.
25///
26/// Creations retain their declared vector order, sends retain their named
27/// product shape, and `become_` is the exact verdict already committed by the
28/// pure behavior fold. The enclosing [`Interpretation`] records whether the
29/// interpreter completed or corrupted while retaining this entire shape.
30#[derive(Clone, Debug, Eq, PartialEq)]
31pub struct ActionSettlement<Creations, Sends, Ph> {
32    pub creations: Creations,
33    pub sends: Sends,
34    pub become_: Become<Ph>,
35}
36
37/// Final settlement of the creation leg of one [`Actions`] value.
38#[derive(Clone, Debug, Eq, PartialEq)]
39pub enum CreationSettlement<Requests, Settlements> {
40    /// The whole batch was routed and every child has an exact settlement.
41    Settled(Settlements),
42    /// Route preparation rejected the complete unchanged batch.
43    Rejected {
44        creations: Requests,
45        reason: ChildNamespaceExhausted,
46    },
47    /// Route preparation corrupted before transferring any request.
48    Corrupt {
49        creations: Requests,
50        fault: InterpreterFault,
51    },
52}
53
54/// Exact creation settlement deliberately retained for actor retirement.
55///
56/// This product distinguishes terminal custody from a settlement that must
57/// return as another input to a live creator. It never erases or reconstructs
58/// the enclosed accepted, rejected, corrupt, or unattempted values.
59#[must_use = "a retirement creation settlement must remain in terminal custody"]
60#[derive(Clone, Debug, Eq, PartialEq)]
61pub struct RetirementCreationSettlement<Settlement> {
62    settlement: Settlement,
63}
64
65impl<Settlement> RetirementCreationSettlement<Settlement> {
66    #[must_use]
67    pub const fn new(settlement: Settlement) -> Self {
68        Self { settlement }
69    }
70
71    #[must_use]
72    pub fn into_settlement(self) -> Settlement {
73        self.settlement
74    }
75}
76
77/// Static complete-settlement product selected by one concrete action type.
78///
79/// This projection performs no interpretation and names no runtime. It lets a
80/// lifecycle host retain the exact result type of an action without rebuilding
81/// its creation or send products.
82pub trait ActionSettlements: Sized {
83    type Settlements;
84    type SourceCustody;
85    type InterpretationCustody;
86    fn prepare_interpretation(
87        progress: &mut Option<
88            InterpretationProgress<Self, Self::InterpretationCustody, Self::Settlements>,
89        >,
90    );
91    fn finish_interpretation(
92        progress: &mut Option<
93            InterpretationProgress<Self, Self::InterpretationCustody, Self::Settlements>,
94        >,
95    );
96}
97
98impl<A, Ph, Sends, Birth> ActionSettlements for Actions<A, Ph, Sends, Birth>
99where
100    A: Address,
101    Sends: SendSettlements,
102    Birth: CreationSettlements<A>,
103{
104    type Settlements = ActionSettlement<Birth::Settlements, Sends::Settlements, Ph>;
105    type SourceCustody = (
106        (
107            Option<SourceProgress<Birth::Settlements, Birth::SourceCustody>>,
108            Option<SourceProgress<Sends::Settlements, Sends::SourceCustody>>,
109        ),
110        Become<Ph>,
111    );
112    type InterpretationCustody = (
113        (
114            Option<
115                InterpretationProgress<
116                    Creations<CreateChild<A, Birth::Child>>,
117                    Birth::InterpretationCustody,
118                    Birth::Settlements,
119                >,
120            >,
121            Option<InterpretationProgress<Sends, Sends::InterpretationCustody, Sends::Settlements>>,
122        ),
123        Become<Ph>,
124    );
125
126    fn prepare_interpretation(
127        progress: &mut Option<
128            InterpretationProgress<Self, Self::InterpretationCustody, Self::Settlements>,
129        >,
130    ) {
131        if matches!(progress, Some(InterpretationProgress::Original(_))) {
132            match progress.take() {
133                Some(InterpretationProgress::Original(Actions {
134                    creates,
135                    sends,
136                    become_,
137                })) => {
138                    *progress = Some(InterpretationProgress::Interpreting((
139                        (
140                            Some(InterpretationProgress::Original(creates)),
141                            Some(InterpretationProgress::Original(sends)),
142                        ),
143                        become_,
144                    )));
145                }
146                retained => *progress = retained,
147            }
148        }
149    }
150
151    fn finish_interpretation(
152        progress: &mut Option<
153            InterpretationProgress<Self, Self::InterpretationCustody, Self::Settlements>,
154        >,
155    ) {
156        if !matches!(
157            progress,
158            Some(InterpretationProgress::Interpreting((
159                (
160                    Some(InterpretationProgress::Completed(_)),
161                    Some(InterpretationProgress::Completed(_))
162                ),
163                _
164            )))
165        ) {
166            return;
167        }
168        // Only structural extraction after every producer was disposed and
169        // every child finalizer borrowed its actual outside slot separately.
170        match progress.take() {
171            Some(InterpretationProgress::Interpreting((
172                (
173                    Some(InterpretationProgress::Completed(creations)),
174                    Some(InterpretationProgress::Completed(sends)),
175                ),
176                become_,
177            ))) => {
178                let completed = match (creations, sends) {
179                    (Interpretation::Complete(creations), Interpretation::Complete(sends)) => {
180                        Interpretation::Complete(ActionSettlement {
181                            creations,
182                            sends,
183                            become_,
184                        })
185                    }
186                    (
187                        Interpretation::Complete(creations) | Interpretation::Corrupt(creations),
188                        Interpretation::Corrupt(sends),
189                    )
190                    | (Interpretation::Corrupt(creations), Interpretation::Complete(sends)) => {
191                        Interpretation::Corrupt(ActionSettlement {
192                            creations,
193                            sends,
194                            become_,
195                        })
196                    }
197                };
198                *progress = Some(InterpretationProgress::Completed(completed));
199            }
200            retained => *progress = retained,
201        }
202    }
203}
204
205/// One exact action-settlement product selected by a concrete behavior.
206///
207/// Runtime lifecycle types use this blanket projection instead of repeating
208/// the behavior's internal send- and birth-product bounds through every parent
209/// and application owner. The associated value remains fully concrete and is
210/// never erased or reclassified.
211pub trait BehaviorSettlements: Behavior {
212    type Settlements;
213    type SourceCustody;
214    type InterpretationCustody;
215}
216
217impl<B> BehaviorSettlements for B
218where
219    B: Behavior,
220    B::Sends: SendSettlements,
221    B::Birth: CreationSettlements<BehaviorAddr<B>>,
222{
223    type Settlements =
224        <Actions<BehaviorAddr<B>, B::Ph, B::Sends, B::Birth> as ActionSettlements>::Settlements;
225    type SourceCustody =
226        <Actions<BehaviorAddr<B>, B::Ph, B::Sends, B::Birth> as ActionSettlements>::SourceCustody;
227    type InterpretationCustody = <Actions<BehaviorAddr<B>, B::Ph, B::Sends, B::Birth> as ActionSettlements>::InterpretationCustody;
228}
229
230impl<Requests, Settlements> ClassifySettlement for CreationSettlement<Requests, Settlements>
231where
232    Settlements: ClassifySettlement,
233{
234    fn settlement_status(&self) -> SettlementStatus {
235        match self {
236            Self::Settled(settlements) => settlements.settlement_status(),
237            Self::Rejected { .. } => SettlementStatus::Rejected,
238            Self::Corrupt { .. } => SettlementStatus::Corrupt,
239        }
240    }
241}
242
243impl<Settlement> ClassifySettlement for RetirementCreationSettlement<Settlement>
244where
245    Settlement: ClassifySettlement,
246{
247    fn settlement_status(&self) -> SettlementStatus {
248        self.settlement.settlement_status()
249    }
250}
251
252impl<Item> ClassifySettlement for Creations<Item>
253where
254    Item: ClassifySettlement,
255{
256    fn settlement_status(&self) -> SettlementStatus {
257        let mut status = SettlementStatus::Accepted;
258        for item in self.iter() {
259            status = status.combine(item.settlement_status());
260        }
261        status
262    }
263}
264
265/// Static creation-settlement product selected by one birth mode.
266pub trait CreationSettlements<A: Address>: BirthMode {
267    type Settlements: ClassifySettlement;
268    type SourceCustody;
269    type InterpretationCustody;
270    fn prepare_interpretation(
271        progress: &mut Option<
272            InterpretationProgress<
273                Creations<CreateChild<A, Self::Child>>,
274                Self::InterpretationCustody,
275                Self::Settlements,
276            >,
277        >,
278    );
279    fn finish_interpretation(
280        progress: &mut Option<
281            InterpretationProgress<
282                Creations<CreateChild<A, Self::Child>>,
283                Self::InterpretationCustody,
284                Self::Settlements,
285            >,
286        >,
287    );
288}
289
290impl<A: Address> CreationSettlements<A> for NoBirths {
291    type Settlements = Creations<Never>;
292    type SourceCustody = Self::Settlements;
293    type InterpretationCustody = ();
294    fn prepare_interpretation(
295        progress: &mut Option<
296            InterpretationProgress<
297                Creations<CreateChild<A, Never>>,
298                Self::InterpretationCustody,
299                Self::Settlements,
300            >,
301        >,
302    ) {
303        match progress.take() {
304            Some(InterpretationProgress::Original(original)) => {
305                let settlements = original
306                    .into_iter()
307                    .map(|creation| {
308                        let (_, never, _) = creation.into_parts();
309                        match never {}
310                    })
311                    .collect();
312                *progress = Some(InterpretationProgress::Completed(Interpretation::Complete(
313                    Creations::from_items(settlements),
314                )));
315            }
316            retained => *progress = retained,
317        }
318    }
319    fn finish_interpretation(
320        progress: &mut Option<
321            InterpretationProgress<
322                Creations<CreateChild<A, Never>>,
323                Self::InterpretationCustody,
324                Self::Settlements,
325            >,
326        >,
327    ) {
328        Self::prepare_interpretation(progress);
329    }
330}
331
332impl<A, C> CreationSettlements<A> for Births<C>
333where
334    A: Address,
335    C: ChildCreationProduct<A, ChildHead>,
336{
337    type Settlements = CreationSettlement<
338        Creations<CreateChild<A, C>>,
339        Creations<
340            SettledItem<
341                RoutedCreation<A, C>,
342                ItemSettlement<
343                    RoutedCreation<A, C>,
344                    <C as ChildCreationProduct<A, ChildHead>>::Result,
345                    CreationRejection,
346                    Never,
347                >,
348            >,
349        >,
350    >;
351    type SourceCustody = (
352        Option<CreationsSettled<A, C>>,
353        Option<Result<(), CreationsSettled<A, C>>>,
354    );
355    type InterpretationCustody = CreationInterpretationCustody<A, C>;
356    fn prepare_interpretation(
357        progress: &mut Option<
358            InterpretationProgress<
359                Creations<CreateChild<A, C>>,
360                Self::InterpretationCustody,
361                Self::Settlements,
362            >,
363        >,
364    ) {
365        prepare_creations(progress);
366    }
367    fn finish_interpretation(
368        progress: &mut Option<
369            InterpretationProgress<
370                Creations<CreateChild<A, C>>,
371                Self::InterpretationCustody,
372                Self::Settlements,
373            >,
374        >,
375    ) {
376        finish_creations(progress);
377    }
378}
379
380impl<A, C> CreationSettlements<A> for RetirementBirths<C>
381where
382    A: Address,
383    C: ChildCreationProduct<A, ChildHead>,
384{
385    type Settlements =
386        RetirementCreationSettlement<<Births<C> as CreationSettlements<A>>::Settlements>;
387    type SourceCustody = Self::Settlements;
388    type InterpretationCustody = Option<
389        InterpretationProgress<
390            Creations<CreateChild<A, C>>,
391            <Births<C> as CreationSettlements<A>>::InterpretationCustody,
392            <Births<C> as CreationSettlements<A>>::Settlements,
393        >,
394    >;
395    fn prepare_interpretation(
396        progress: &mut Option<
397            InterpretationProgress<
398                Creations<CreateChild<A, C>>,
399                Self::InterpretationCustody,
400                Self::Settlements,
401            >,
402        >,
403    ) {
404        match progress.take() {
405            Some(InterpretationProgress::Original(original)) => {
406                *progress = Some(InterpretationProgress::Interpreting(Some(
407                    InterpretationProgress::Original(original),
408                )))
409            }
410            retained => *progress = retained,
411        }
412    }
413    fn finish_interpretation(
414        progress: &mut Option<
415            InterpretationProgress<
416                Creations<CreateChild<A, C>>,
417                Self::InterpretationCustody,
418                Self::Settlements,
419            >,
420        >,
421    ) {
422        // Base finalization has already loaned the outside base progress.
423        // This extraction calls no generic/custom method.
424        match progress.take() {
425            Some(InterpretationProgress::Interpreting(Some(
426                InterpretationProgress::Completed(received),
427            ))) => {
428                *progress = Some(InterpretationProgress::Completed(
429                    received.map(RetirementCreationSettlement::new),
430                ));
431            }
432            retained => *progress = retained,
433        }
434    }
435}
436
437/// One complete creation batch returned to its live creator.
438///
439/// The value retains either every routed child settlement or the entire
440/// unchanged batch rejected during route preparation. It is interpreter-facing
441/// custody, not an application protocol or another creation operation.
442#[must_use = "a returned creation batch must be admitted or retained"]
443pub struct CreationsSettled<A, C>
444where
445    A: Address,
446    C: ChildCreationProduct<A, ChildHead>,
447{
448    settlement: <Births<C> as CreationSettlements<A>>::Settlements,
449}
450
451/// Complete event algebra for an ordinary creator that receives its exact
452/// creation settlements as a later behavior input.
453///
454/// The creation source is selected semantically by [`Births<C>`]. Outer
455/// behavior layers lift that ingress through their existing event composition;
456/// no structural path is exposed to the actor author.
457pub enum CreationEvent<A, C, M>
458where
459    A: Address,
460    C: ChildCreationProduct<A, ChildHead>,
461{
462    Settlements(CreationsSettled<A, C>),
463    User(User<A, M>),
464}
465
466impl<A, C, M> EventIngress<Births<C>, CreationsSettled<A, C>> for CreationEvent<A, C, M>
467where
468    A: Address,
469    C: ChildCreationProduct<A, ChildHead>,
470{
471    fn ingress(input: CreationsSettled<A, C>) -> Self {
472        Self::Settlements(input)
473    }
474}
475
476impl<A, C, M> UserEvent for CreationEvent<A, C, M>
477where
478    A: Address,
479    C: ChildCreationProduct<A, ChildHead>,
480{
481    type Addr = A;
482    type Message = M;
483
484    fn user(from: A, message: M) -> Self {
485        Self::User(User::new(from, message))
486    }
487
488    fn into_user(self) -> Result<User<A, M>, Self> {
489        match self {
490            Self::User(event) => Ok(event),
491            settlements @ Self::Settlements(_) => Err(settlements),
492        }
493    }
494}
495
496impl<A, C> CreationsSettled<A, C>
497where
498    A: Address,
499    C: ChildCreationProduct<A, ChildHead>,
500{
501    #[must_use]
502    pub const fn new(settlement: <Births<C> as CreationSettlements<A>>::Settlements) -> Self {
503        Self { settlement }
504    }
505
506    #[must_use]
507    pub fn into_settlement(self) -> <Births<C> as CreationSettlements<A>>::Settlements {
508        self.settlement
509    }
510}
511
512impl<Host, RootEvent> SourceSettlementCustody<Host, RootEvent> for Creations<Never> {
513    type Custody = Self;
514    fn prepare_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
515        *progress = match progress.take() {
516            Some(SourceProgress::Original(original)) => Some(SourceProgress::Completed(
517                SourceCustody::Exhausted(original),
518            )),
519            progress => progress,
520        };
521    }
522    fn offer_next_to_source(
523        _: &mut Self::Custody,
524        _: &mut Host,
525    ) -> impl Future<Output = ()> + Send {
526        future::ready(())
527    }
528    fn finish_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
529        *progress = match progress.take() {
530            Some(SourceProgress::Original(original) | SourceProgress::Offering(original)) => Some(
531                SourceProgress::Completed(SourceCustody::Exhausted(original)),
532            ),
533            retained => retained,
534        };
535    }
536}
537
538impl<A, C, Host, RootEvent> SourceSettlementCustody<Host, RootEvent>
539    for CreationSettlement<
540        Creations<CreateChild<A, C>>,
541        Creations<
542            SettledItem<
543                RoutedCreation<A, C>,
544                ItemSettlement<
545                    RoutedCreation<A, C>,
546                    <C as ChildCreationProduct<A, ChildHead>>::Result,
547                    CreationRejection,
548                    Never,
549                >,
550            >,
551        >,
552    >
553where
554    A: Address,
555    A::Nonce: Send,
556    C: ChildCreationProduct<A, ChildHead> + Send,
557    <C as ChildCreationProduct<A, ChildHead>>::Result: Send,
558    Host: SourceAdmission<RootEvent, Births<C>, CreationsSettled<A, C>>,
559    RootEvent: crate::EventIngress<Births<C>, CreationsSettled<A, C>>,
560{
561    type Custody = (
562        Option<CreationsSettled<A, C>>,
563        Option<Result<(), CreationsSettled<A, C>>>,
564    );
565    fn prepare_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
566        *progress = match progress.take() {
567            Some(SourceProgress::Original(original)) if matches!(&original, CreationSettlement::Settled(settlements) if settlements.is_empty()) => {
568                Some(SourceProgress::Completed(SourceCustody::Exhausted(
569                    original,
570                )))
571            }
572            Some(SourceProgress::Original(original)) => Some(SourceProgress::Offering((
573                Some(CreationsSettled::new(original)),
574                None,
575            ))),
576            progress => progress,
577        };
578    }
579    fn offer_next_to_source(
580        custody: &mut Self::Custody,
581        host: &mut Host,
582    ) -> impl Future<Output = ()> + Send {
583        async move {
584            if custody.1.is_none() {
585                host.admit_source(&mut custody.0, &mut custody.1).await;
586            }
587        }
588    }
589    fn finish_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
590        *progress = match progress.take() {
591            Some(SourceProgress::Offering((None, Some(Ok(()))))) => {
592                Some(SourceProgress::Completed(SourceCustody::Admitted(
593                    CreationSettlement::Settled(Creations::empty()),
594                )))
595            }
596            Some(SourceProgress::Offering((None, Some(Err(returned))))) => Some(
597                SourceProgress::Completed(SourceCustody::Closed(returned.into_settlement())),
598            ),
599            progress => progress,
600        };
601    }
602}
603
604impl<A, C, Host, RootEvent> SourceSettlementCustody<Host, RootEvent>
605    for RetirementCreationSettlement<
606        CreationSettlement<
607            Creations<CreateChild<A, C>>,
608            Creations<
609                SettledItem<
610                    RoutedCreation<A, C>,
611                    ItemSettlement<
612                        RoutedCreation<A, C>,
613                        <C as ChildCreationProduct<A, ChildHead>>::Result,
614                        CreationRejection,
615                        Never,
616                    >,
617                >,
618            >,
619        >,
620    >
621where
622    A: Address,
623    A::Nonce: Send,
624    C: ChildCreationProduct<A, ChildHead> + Send,
625    <C as ChildCreationProduct<A, ChildHead>>::Result: Send,
626{
627    type Custody = Self;
628    fn prepare_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
629        *progress = match progress.take() {
630            Some(SourceProgress::Original(original)) if matches!(&original.settlement, CreationSettlement::Settled(settlements) if settlements.is_empty()) => {
631                Some(SourceProgress::Completed(SourceCustody::Exhausted(
632                    original,
633                )))
634            }
635            Some(SourceProgress::Original(original)) => {
636                Some(SourceProgress::Completed(SourceCustody::Retained(original)))
637            }
638            progress => progress,
639        };
640    }
641    fn offer_next_to_source(
642        _: &mut Self::Custody,
643        _: &mut Host,
644    ) -> impl Future<Output = ()> + Send {
645        future::ready(())
646    }
647    fn finish_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
648        *progress = match progress.take() {
649            Some(SourceProgress::Original(original) | SourceProgress::Offering(original)) if matches!(&original.settlement, CreationSettlement::Settled(settlements) if settlements.is_empty()) => {
650                Some(SourceProgress::Completed(SourceCustody::Exhausted(
651                    original,
652                )))
653            }
654            Some(SourceProgress::Original(original) | SourceProgress::Offering(original)) => {
655                Some(SourceProgress::Completed(SourceCustody::Retained(original)))
656            }
657            retained => retained,
658        };
659    }
660}
661
662/// Outside custody of the actual routing reply or ordered child attempts.
663/// Native causes remain runtime retirement facts, not this normal typed product.
664pub enum CreationInterpretationCustody<A, C>
665where
666    A: Address,
667    C: ChildCreationProduct<A, ChildHead>,
668{
669    Routing {
670        input: Option<Creations<CreateChild<A, C>>>,
671        received: Option<
672            ItemSettlement<
673                Creations<CreateChild<A, C>>,
674                Creations<RoutedCreation<A, C>>,
675                ChildNamespaceExhausted,
676                Never,
677            >,
678        >,
679    },
680    Children(
681        Vec<(
682            Option<RoutedCreation<A, C>>,
683            Option<
684                ItemSettlement<
685                    RoutedCreation<A, C>,
686                    <C as ChildCreationProduct<A, ChildHead>>::Result,
687                    CreationRejection,
688                    Never,
689                >,
690            >,
691        )>,
692    ),
693}
694
695fn prepare_creations<A, C>(
696    progress: &mut Option<
697        InterpretationProgress<
698            Creations<CreateChild<A, C>>,
699            CreationInterpretationCustody<A, C>,
700            CreationSettlement<
701                Creations<CreateChild<A, C>>,
702                Creations<
703                    SettledItem<
704                        RoutedCreation<A, C>,
705                        ItemSettlement<
706                            RoutedCreation<A, C>,
707                            <C as ChildCreationProduct<A, ChildHead>>::Result,
708                            CreationRejection,
709                            Never,
710                        >,
711                    >,
712                >,
713            >,
714        >,
715    >,
716) where
717    A: Address,
718    C: ChildCreationProduct<A, ChildHead>,
719{
720    if matches!(progress, Some(InterpretationProgress::Original(_))) {
721        match progress.take() {
722            Some(InterpretationProgress::Original(input)) => {
723                *progress = Some(InterpretationProgress::Interpreting(
724                    CreationInterpretationCustody::Routing {
725                        input: Some(input),
726                        received: None,
727                    },
728                ));
729            }
730            retained => *progress = retained,
731        }
732    }
733}
734
735fn finish_routing<A, C>(
736    progress: &mut Option<
737        InterpretationProgress<
738            Creations<CreateChild<A, C>>,
739            CreationInterpretationCustody<A, C>,
740            <Births<C> as CreationSettlements<A>>::Settlements,
741        >,
742    >,
743) where
744    A: Address,
745    C: ChildCreationProduct<A, ChildHead>,
746{
747    if !matches!(
748        progress,
749        Some(InterpretationProgress::Interpreting(
750            CreationInterpretationCustody::Routing {
751                input: None,
752                received: Some(_)
753            }
754        ))
755    ) {
756        return;
757    }
758    match progress.take() {
759        Some(InterpretationProgress::Interpreting(CreationInterpretationCustody::Routing {
760            input: None,
761            received: Some(received),
762        })) => {
763            *progress = Some(match received {
764                ItemSettlement::Accepted(routed) => {
765                    InterpretationProgress::Interpreting(CreationInterpretationCustody::Children(
766                        routed
767                            .into_iter()
768                            .map(|input| (Some(input), None))
769                            .collect(),
770                    ))
771                }
772                ItemSettlement::Rejected { item, reason } => InterpretationProgress::Completed(
773                    Interpretation::Complete(CreationSettlement::Rejected {
774                        creations: item,
775                        reason,
776                    }),
777                ),
778                ItemSettlement::Corrupt { item, fault } => InterpretationProgress::Completed(
779                    Interpretation::Corrupt(CreationSettlement::Corrupt {
780                        creations: item,
781                        fault,
782                    }),
783                ),
784                ItemSettlement::Blocked { prerequisite, .. } => match prerequisite {},
785            });
786        }
787        retained => *progress = retained,
788    }
789}
790
791async fn settle_child<A, C, Interpreter>(
792    input: &mut Option<RoutedCreation<A, C>>,
793    received: &mut Option<
794        ItemSettlement<
795            RoutedCreation<A, C>,
796            <C as ChildCreationProduct<A, ChildHead>>::Result,
797            CreationRejection,
798            Never,
799        >,
800    >,
801    interpreter: &mut Interpreter,
802) where
803    A: Address,
804    C: ChildCreationProduct<A, ChildHead> + DispatchBirth<A, Interpreter>,
805{
806    if received.is_some() {
807        return;
808    }
809    let Some(original) = input.take() else {
810        return;
811    };
812    let (creation, route) = original.into_parts();
813    let (id, child, kind) = creation.into_parts();
814    let mut attempt = pin!(child.dispatch_birth(id, route, kind, interpreter));
815    future::poll_fn(|context| match attempt.as_mut().poll(context) {
816        Poll::Pending => Poll::Pending,
817        Poll::Ready(reply) => {
818            *received = Some(reply);
819            Poll::Ready(())
820        }
821    })
822    .await;
823}
824
825async fn interpret_creations<A, C, Interpreter, RootEvent, Path>(
826    progress: &mut Option<
827        InterpretationProgress<
828            Creations<CreateChild<A, C>>,
829            CreationInterpretationCustody<A, C>,
830            <Births<C> as CreationSettlements<A>>::Settlements,
831        >,
832    >,
833    interpreter: &mut Interpreter,
834) where
835    A: Address,
836    A::Nonce: Send,
837    C: ChildCreationProduct<A, ChildHead> + DispatchBirth<A, Interpreter> + Send,
838    <C as ChildCreationProduct<A, ChildHead>>::Result: Send,
839    Interpreter: InterpretItem<Creations<CreateChild<A, C>>, RootEvent, Path> + Send,
840{
841    prepare_creations(progress);
842    if let Some(InterpretationProgress::Interpreting(CreationInterpretationCustody::Routing {
843        input,
844        received,
845    })) = progress
846    {
847        <Interpreter as InterpretItem<Creations<CreateChild<A,C>>, RootEvent, Path>>::interpret_item(
848            interpreter, input, received,
849        ).await;
850    }
851    finish_routing(progress);
852    let Some(InterpretationProgress::Interpreting(CreationInterpretationCustody::Children(rows))) =
853        progress
854    else {
855        return;
856    };
857    for (input, received) in rows {
858        match (input.as_ref(), received.as_ref()) {
859            (None, Some(ItemSettlement::Corrupt { .. })) => break,
860            (None, Some(_)) => continue,
861            (Some(_), None) => {}
862            _ => return,
863        }
864        settle_child(input, received, interpreter).await;
865        match (input.as_ref(), received.as_ref()) {
866            (None, Some(ItemSettlement::Corrupt { .. })) => break,
867            (None, Some(_)) => {}
868            _ => return,
869        }
870    }
871    finish_creations(progress);
872}
873
874fn finish_creations<A, C>(
875    progress: &mut Option<
876        InterpretationProgress<
877            Creations<CreateChild<A, C>>,
878            CreationInterpretationCustody<A, C>,
879            <Births<C> as CreationSettlements<A>>::Settlements,
880        >,
881    >,
882) where
883    A: Address,
884    C: ChildCreationProduct<A, ChildHead>,
885{
886    finish_routing(progress);
887    let Some(InterpretationProgress::Interpreting(CreationInterpretationCustody::Children(rows))) =
888        progress.as_ref()
889    else {
890        return;
891    };
892    let corrupt = rows
893        .iter()
894        .position(|(_, received)| matches!(received, Some(ItemSettlement::Corrupt { .. })));
895    let complete = rows.iter().enumerate().all(|(index, (input, received))| {
896        if corrupt.is_some_and(|corrupt| index > corrupt) {
897            input.is_some() && received.is_none()
898        } else {
899            input.is_none() && received.is_some()
900        }
901    });
902    if !complete {
903        return;
904    }
905    let Some(InterpretationProgress::Interpreting(CreationInterpretationCustody::Children(rows))) =
906        progress.take()
907    else {
908        return;
909    };
910    let mut remaining = rows.into_iter();
911    let mut settled = Vec::with_capacity(remaining.len());
912    while let Some((input, received)) = remaining.next() {
913        match (input, received) {
914            (None, Some(received)) => settled.push(SettledItem::Attempted(received)),
915            (Some(input), None) => settled.push(SettledItem::Unattempted(input)),
916            (input, received) => {
917                let rows = settled
918                    .into_iter()
919                    .map(|settled| match settled {
920                        SettledItem::Attempted(received) => (None, Some(received)),
921                        SettledItem::Unattempted(input) => (Some(input), None),
922                    })
923                    .chain(core::iter::once((input, received)))
924                    .chain(remaining)
925                    .collect();
926                *progress = Some(InterpretationProgress::Interpreting(
927                    CreationInterpretationCustody::Children(rows),
928                ));
929                return;
930            }
931        }
932    }
933    let settlement = CreationSettlement::Settled(Creations::from_items(settled));
934    *progress = Some(InterpretationProgress::Completed(match corrupt {
935        Some(_) => Interpretation::Corrupt(settlement),
936        None => Interpretation::Complete(settlement),
937    }));
938}
939
940/// Generic interpretation of the one creation leg selected by a birth mode.
941///
942/// `NoBirths` needs no runtime capability. `Births<C>` performs one real batch
943/// route attempt followed by independent child establishment in declared order.
944pub trait InterpretCreations<A, Interpreter, RootEvent, Path>: CreationSettlements<A>
945where
946    A: Address,
947{
948    fn interpret_creations(
949        progress: &mut Option<
950            InterpretationProgress<
951                Creations<CreateChild<A, Self::Child>>,
952                Self::InterpretationCustody,
953                Self::Settlements,
954            >,
955        >,
956        interpreter: &mut Interpreter,
957    ) -> impl Future<Output = ()> + Send;
958}
959
960impl<A, Interpreter, RootEvent, Path> InterpretCreations<A, Interpreter, RootEvent, Path>
961    for NoBirths
962where
963    A: Address,
964    Creations<CreateChild<A, Never>>: Send,
965{
966    fn interpret_creations(
967        progress: &mut Option<
968            InterpretationProgress<
969                Creations<CreateChild<A, Never>>,
970                Self::InterpretationCustody,
971                Self::Settlements,
972            >,
973        >,
974        _: &mut Interpreter,
975    ) -> impl Future<Output = ()> + Send {
976        async move {
977            <Self as CreationSettlements<A>>::prepare_interpretation(progress);
978        }
979    }
980}
981
982impl<A, C, Interpreter, RootEvent, Path> InterpretCreations<A, Interpreter, RootEvent, Path>
983    for Births<C>
984where
985    A: Address,
986    A::Nonce: Send,
987    C: ChildCreationProduct<A, ChildHead> + DispatchBirth<A, Interpreter> + Send,
988    <C as ChildCreationProduct<A, ChildHead>>::Result: Send,
989    Interpreter: InterpretItem<Creations<CreateChild<A, C>>, RootEvent, Path> + Send,
990{
991    fn interpret_creations(
992        progress: &mut Option<
993            InterpretationProgress<
994                Creations<CreateChild<A, C>>,
995                Self::InterpretationCustody,
996                Self::Settlements,
997            >,
998        >,
999        interpreter: &mut Interpreter,
1000    ) -> impl Future<Output = ()> + Send {
1001        interpret_creations::<A, C, Interpreter, RootEvent, Path>(progress, interpreter)
1002    }
1003}
1004
1005impl<A, C, Interpreter, RootEvent, Path> InterpretCreations<A, Interpreter, RootEvent, Path>
1006    for RetirementBirths<C>
1007where
1008    A: Address,
1009    C: ChildCreationProduct<A, ChildHead>,
1010    Births<C>: BirthMode<Child = C> + InterpretCreations<A, Interpreter, RootEvent, Path>,
1011    Creations<CreateChild<A, C>>: Send,
1012    <Births<C> as CreationSettlements<A>>::InterpretationCustody: Send,
1013    <Births<C> as CreationSettlements<A>>::Settlements: Send,
1014    Interpreter: Send,
1015{
1016    fn interpret_creations(
1017        progress: &mut Option<
1018            InterpretationProgress<
1019                Creations<CreateChild<A, C>>,
1020                Self::InterpretationCustody,
1021                Self::Settlements,
1022            >,
1023        >,
1024        interpreter: &mut Interpreter,
1025    ) -> impl Future<Output = ()> + Send {
1026        async move {
1027            <Self as CreationSettlements<A>>::prepare_interpretation(progress);
1028            let Some(InterpretationProgress::Interpreting(base)) = progress else {
1029                return;
1030            };
1031            <Births<C> as InterpretCreations<A,Interpreter,RootEvent,Path>>::interpret_creations(base,interpreter).await;
1032            <Births<C> as CreationSettlements<A>>::finish_interpretation(base);
1033            <Self as CreationSettlements<A>>::finish_interpretation(progress);
1034        }
1035    }
1036}
1037
1038impl<Creations, Sends, Ph> ClassifySettlement for ActionSettlement<Creations, Sends, Ph>
1039where
1040    Creations: ClassifySettlement,
1041    Sends: ClassifySettlement,
1042{
1043    fn settlement_status(&self) -> SettlementStatus {
1044        self.creations
1045            .settlement_status()
1046            .combine(self.sends.settlement_status())
1047    }
1048}
1049
1050impl<Host, RootEvent, Creations, Sends, Ph> SourceSettlementCustody<Host, RootEvent>
1051    for ActionSettlement<Creations, Sends, Ph>
1052where
1053    Host: Send,
1054    Creations: SourceSettlementCustody<Host, RootEvent> + Send,
1055    Sends: SourceSettlementCustody<Host, RootEvent> + Send,
1056    Ph: Send,
1057{
1058    type Custody = (
1059        <(Creations, Sends) as SourceSettlementCustody<Host, RootEvent>>::Custody,
1060        Become<Ph>,
1061    );
1062    fn prepare_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
1063        *progress = match progress.take() {
1064            Some(SourceProgress::Original(original)) => Some(SourceProgress::Offering((
1065                (
1066                    Some(SourceProgress::Original(original.creations)),
1067                    Some(SourceProgress::Original(original.sends)),
1068                ),
1069                original.become_,
1070            ))),
1071            progress => progress,
1072        };
1073    }
1074    fn offer_next_to_source(
1075        custody: &mut Self::Custody,
1076        host: &mut Host,
1077    ) -> impl Future<Output = ()> + Send {
1078        offer_source_in_order::<Host, RootEvent, Creations, Sends>(&mut custody.0, host)
1079    }
1080    fn finish_source(progress: &mut Option<SourceProgress<Self, Self::Custody>>) {
1081        let (custody, become_) = match progress.take() {
1082            Some(SourceProgress::Offering(custody)) => custody,
1083            retained => {
1084                *progress = retained;
1085                return;
1086            }
1087        };
1088        let mut product = Some(SourceProgress::Offering(custody));
1089        <(Creations, Sends) as SourceSettlementCustody<Host, RootEvent>>::finish_source(
1090            &mut product,
1091        );
1092        *progress = match product {
1093            Some(SourceProgress::Original((creations, sends))) => {
1094                Some(SourceProgress::Original(Self {
1095                    creations,
1096                    sends,
1097                    become_,
1098                }))
1099            }
1100            Some(SourceProgress::Offering(custody)) => {
1101                Some(SourceProgress::Offering((custody, become_)))
1102            }
1103            Some(SourceProgress::Completed(reply)) => Some(SourceProgress::Completed(reply.map(
1104                |(creations, sends)| Self {
1105                    creations,
1106                    sends,
1107                    become_,
1108                },
1109            ))),
1110            None => None,
1111        };
1112    }
1113}
1114
1115/// Capability to append one communication at a statically selected lane while
1116/// preserving every other actor-transition effect.
1117pub trait AppendSend<Input, Path>: Sized {
1118    /// Append `input` exactly once and preserve creations and the next verdict.
1119    #[must_use]
1120    fn append_send(self, input: Input) -> Self;
1121}
1122
1123/// Bombay's typed realization of the actor transition effects: communications,
1124/// fresh actor creation, and next behavior or termination.
1125///
1126/// [`Actions::interpret`] resolves every fresh creation in `creates` before the
1127/// named `sends` product. A committed resolution installs and binds the child;
1128/// a semantic rejection binds nothing but remains an accepted interpretation
1129/// receipt. This ordering lets a same-action [`crate::ChildDelivery`] or typed
1130/// creation-observation request use the authoritative result rather than
1131/// Behavior's intent. Each child-operation interpreter returns `Blocked` only
1132/// when its exact binding prerequisite rejected, while independent later items
1133/// are still attempted. Interpreter corruption retains the factual prefix and
1134/// every exact remaining value. No later creation may inherit a failed binding.
1135/// Creation order is vector order, and named send products declare their own
1136/// stable order. Constructing a value remains pure.
1137#[must_use = "actor transition effects must be interpreted, inspected, or retained"]
1138pub struct Actions<A: Address, Ph, Sends, Birth: BirthMode> {
1139    pub sends: Sends,
1140    pub creates: Creations<CreateChild<A, Birth::Child>>,
1141    pub become_: Become<Ph>,
1142}
1143
1144impl<A, Ph, Sends, Birth> core::fmt::Debug for Actions<A, Ph, Sends, Birth>
1145where
1146    A: Address + core::fmt::Debug,
1147    A::Nonce: core::fmt::Debug,
1148    Ph: core::fmt::Debug,
1149    Sends: core::fmt::Debug,
1150    Birth: BirthMode,
1151    Birth::Child: core::fmt::Debug,
1152{
1153    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1154        formatter
1155            .debug_struct("Actions")
1156            .field("sends", &self.sends)
1157            .field("creates", &self.creates)
1158            .field("become", &self.become_)
1159            .finish()
1160    }
1161}
1162
1163impl<A, Ph, Sends, Birth> PartialEq for Actions<A, Ph, Sends, Birth>
1164where
1165    A: Address + PartialEq,
1166    A::Nonce: PartialEq,
1167    Ph: PartialEq,
1168    Sends: PartialEq,
1169    Birth: BirthMode,
1170    Birth::Child: PartialEq,
1171{
1172    fn eq(&self, other: &Self) -> bool {
1173        self.sends == other.sends && self.creates == other.creates && self.become_ == other.become_
1174    }
1175}
1176
1177impl<A, Ph, Sends, Birth> Eq for Actions<A, Ph, Sends, Birth>
1178where
1179    A: Address + Eq,
1180    A::Nonce: Eq,
1181    Ph: Eq,
1182    Sends: Eq,
1183    Birth: BirthMode,
1184    Birth::Child: Eq,
1185{
1186}
1187
1188impl<A: Address, Ph, Sends, Birth: BirthMode> Actions<A, Ph, Sends, Birth> {
1189    /// Transform only the send effects, preserving creation order and the
1190    /// next-behavior verdict exactly.
1191    #[must_use]
1192    pub fn map_sends<Mapped>(
1193        self,
1194        map: impl FnOnce(Sends) -> Mapped,
1195    ) -> Actions<A, Ph, Mapped, Birth> {
1196        Actions {
1197            sends: map(self.sends),
1198            creates: self.creates,
1199            become_: self.become_,
1200        }
1201    }
1202
1203    /// Transform only the next-behavior verdict, preserving sends and
1204    /// creation order exactly.
1205    #[must_use]
1206    pub fn map_become<NextPh>(
1207        self,
1208        map: impl FnOnce(Become<Ph>) -> Become<NextPh>,
1209    ) -> Actions<A, NextPh, Sends, Birth> {
1210        Actions {
1211            sends: self.sends,
1212            creates: self.creates,
1213            become_: map(self.become_),
1214        }
1215    }
1216
1217    /// Append one communication to its statically selected send lane.
1218    ///
1219    /// This is a pure transformation of the send leg. It preserves creation
1220    /// order and the exact continue, phase-change, or termination verdict.
1221    /// `Path` is compile-time lane evidence; no runtime lookup is performed.
1222    #[must_use]
1223    pub fn with_send<Input, Path>(mut self, input: Input) -> Self
1224    where
1225        Sends: SendInput<Input, Path>,
1226    {
1227        <Sends as SendInput<Input, Path>>::emit(&mut self.sends, input);
1228        self
1229    }
1230
1231    /// Borrow caller-owned action progress through one statically typed runtime.
1232    ///
1233    /// Creation items are attempted in vector order before the named send
1234    /// product. Lawful creation rejection or blocking is retained and does not
1235    /// suppress sends; concrete child-operation interpreters decide whether an
1236    /// exact binding prerequisite was satisfied. Interpreter corruption stops
1237    /// traversal and retains every later creation and the complete send product
1238    /// as unattempted. The next-behavior verdict is never reconstructed.
1239    pub async fn interpret<Interpreter, RootEvent, Path>(
1240        progress: &mut Option<
1241            InterpretationProgress<
1242                Self,
1243                <Self as ActionSettlements>::InterpretationCustody,
1244                <Self as ActionSettlements>::Settlements,
1245            >,
1246        >,
1247        interpreter: &mut Interpreter,
1248    ) where
1249        Ph: Send,
1250        Sends: InterpretSends<Interpreter, RootEvent, Path>,
1251        Birth: InterpretCreations<A, Interpreter, RootEvent, Path>,
1252        Self: Send,
1253        <Self as ActionSettlements>::Settlements: Send,
1254        <Self as ActionSettlements>::InterpretationCustody: Send,
1255        Interpreter: Send,
1256    {
1257        Self::prepare_interpretation(progress);
1258        let Some(InterpretationProgress::Interpreting(((creates, sends), _))) = progress else {
1259            return;
1260        };
1261        Birth::interpret_creations(creates, interpreter).await;
1262        Birth::finish_interpretation(creates);
1263        match creates {
1264            Some(InterpretationProgress::Completed(Interpretation::Complete(_))) => {
1265                Sends::interpret(sends, interpreter).await;
1266                Sends::finish_interpretation(sends);
1267            }
1268            Some(InterpretationProgress::Completed(Interpretation::Corrupt(_))) => {
1269                Sends::unattempted(sends);
1270            }
1271            // Neither source closure, normal exhaustion, interpreter fault,
1272            // nor a completed settlement can be inferred from a partial slot.
1273            _ => return,
1274        }
1275        Self::finish_interpretation(progress);
1276    }
1277}
1278
1279impl<A, Ph, Sends, Birth, Input, Path> AppendSend<Input, Path> for Actions<A, Ph, Sends, Birth>
1280where
1281    A: Address,
1282    Birth: BirthMode,
1283    Sends: SendInput<Input, Path>,
1284{
1285    fn append_send(self, input: Input) -> Self {
1286        self.with_send::<Input, Path>(input)
1287    }
1288}
1289
1290impl<A: Address, Ph, Sends: SendEffects, Birth: BirthMode> Actions<A, Ph, Sends, Birth> {
1291    #[must_use]
1292    pub const fn new(
1293        sends: Sends,
1294        creates: Creations<CreateChild<A, Birth::Child>>,
1295        become_: Become<Ph>,
1296    ) -> Self {
1297        Self {
1298            sends,
1299            creates,
1300            become_,
1301        }
1302    }
1303
1304    #[must_use]
1305    pub fn just(become_: Become<Ph>) -> Self {
1306        Self {
1307            sends: Sends::empty(),
1308            creates: Creations::empty(),
1309            become_,
1310        }
1311    }
1312
1313    #[must_use]
1314    pub fn cont() -> Self {
1315        Self::just(Step::Continue)
1316    }
1317    #[must_use]
1318    pub fn stop() -> Self {
1319        Self::just(Step::Stop(Stopped))
1320    }
1321    #[must_use]
1322    pub fn goto(phase: Ph) -> Self {
1323        Self::just(Step::Goto(phase))
1324    }
1325
1326    /// Continue after emitting the complete declared send product.
1327    #[must_use]
1328    pub fn send(sends: Sends) -> Self {
1329        Self::new(sends, Creations::empty(), Step::Continue)
1330    }
1331
1332    /// Continue after staging the complete declared creation product.
1333    #[must_use]
1334    pub fn create(creates: Creations<CreateChild<A, Birth::Child>>) -> Self {
1335        Self::new(Sends::empty(), creates, Step::Continue)
1336    }
1337}
1338
1339impl<A: Address, Ph, Sends, Birth: BirthMode>
1340    From<(Sends, Creations<CreateChild<A, Birth::Child>>, Become<Ph>)>
1341    for Actions<A, Ph, Sends, Birth>
1342{
1343    fn from(
1344        (sends, creates, become_): (Sends, Creations<CreateChild<A, Birth::Child>>, Become<Ph>),
1345    ) -> Self {
1346        Self {
1347            sends,
1348            creates,
1349            become_,
1350        }
1351    }
1352}
1353
1354pub type Acted<A, Ph, Sends, Birth, E> = Result<Actions<A, Ph, Sends, Birth>, E>;
1355
1356#[cfg(test)]
1357mod tests {
1358    use super::*;
1359    use crate::{Births, CreationSequence, MailAddr, NoBirths, Own};
1360
1361    fn two_ids() -> (crate::CreationId, crate::CreationId) {
1362        let mut sequence = CreationSequence::new();
1363        let first = sequence.issue().expect("the first creation ID exists");
1364        let second = sequence.issue().expect("the second creation ID exists");
1365        (first, second)
1366    }
1367
1368    #[test]
1369    fn equality_and_debug_cover_every_named_effect_leg() {
1370        type Plain = Actions<MailAddr, u8, Vec<u8>, NoBirths>;
1371        type Creating = Actions<MailAddr, Never, Vec<u8>, Births<u8>>;
1372
1373        let value = Plain::new(vec![1], Creations::empty(), Step::Goto(3));
1374        assert_eq!(
1375            value,
1376            Plain::new(vec![1], Creations::empty(), Step::Goto(3))
1377        );
1378        assert_ne!(
1379            value,
1380            Plain::new(vec![2], Creations::empty(), Step::Goto(3))
1381        );
1382        assert_ne!(
1383            value,
1384            Plain::new(vec![1], Creations::empty(), Step::Goto(4))
1385        );
1386        assert_ne!(
1387            value,
1388            Plain::new(vec![1], Creations::empty(), Step::Continue)
1389        );
1390        assert_eq!(
1391            format!("{value:?}"),
1392            "Actions { sends: [1], creates: Creations { items: [] }, become: Goto(3) }"
1393        );
1394
1395        let (first, second) = two_ids();
1396        let created = Creating::new(
1397            Vec::new(),
1398            Creations::one(CreateChild::birth(first, 9)),
1399            Step::Continue,
1400        );
1401        let other_creation = Creating::new(
1402            Vec::new(),
1403            Creations::one(CreateChild::birth(second, 9)),
1404            Step::Continue,
1405        );
1406        assert_ne!(created, other_creation);
1407    }
1408
1409    #[test]
1410    fn mapping_sends_preserves_creation_order_and_verdict() {
1411        let (first, second) = two_ids();
1412        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
1413            vec![1, 2],
1414            Creations::one(CreateChild::birth(first, ())).and(CreateChild::replacement(
1415                second,
1416                first,
1417                (),
1418            )),
1419            Step::Goto(7),
1420        );
1421
1422        let mapped = actions.map_sends(|sends| sends.len());
1423        assert_eq!(mapped.sends, 2);
1424        assert_eq!(
1425            mapped
1426                .creates
1427                .iter()
1428                .map(CreateChild::id)
1429                .collect::<Vec<_>>(),
1430            [first, second]
1431        );
1432        assert!(matches!(mapped.become_, Step::Goto(7)));
1433    }
1434
1435    #[test]
1436    fn mapping_become_preserves_sends_and_creation_order() {
1437        let (first, _) = two_ids();
1438        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
1439            vec![1, 2],
1440            Creations::one(CreateChild::birth(first, ())),
1441            Step::Goto(7),
1442        );
1443
1444        let mapped: Actions<MailAddr, Never, Vec<u8>, Births<()>> =
1445            actions.map_become(|_| Step::Stop(Stopped));
1446        assert_eq!(mapped.sends, [1, 2]);
1447        let mapped_creation_id = mapped.creates.iter().next().map(CreateChild::id);
1448        assert_eq!(mapped_creation_id, Some(first));
1449        assert!(matches!(mapped.become_, Step::Stop(Stopped)));
1450    }
1451
1452    #[test]
1453    fn fluent_send_changes_only_the_selected_effect_leg() {
1454        let (first, second) = two_ids();
1455        let actions: Actions<MailAddr, u8, Vec<u8>, Births<()>> = Actions::new(
1456            vec![1],
1457            Creations::one(CreateChild::birth(first, ())).and(CreateChild::replacement(
1458                second,
1459                first,
1460                (),
1461            )),
1462            Step::Goto(7),
1463        )
1464        .with_send::<_, Own>(2)
1465        .with_send::<_, Own>(3);
1466
1467        assert_eq!(actions.sends, [1, 2, 3]);
1468        assert_eq!(
1469            actions
1470                .creates
1471                .iter()
1472                .map(CreateChild::id)
1473                .collect::<Vec<_>>(),
1474            [first, second]
1475        );
1476        assert!(matches!(actions.become_, Step::Goto(7)));
1477
1478        let stopped: Actions<MailAddr, Never, Vec<u8>, NoBirths> =
1479            Actions::stop().with_send::<_, Own>(5);
1480        assert_eq!(stopped.sends, [5]);
1481        assert!(matches!(stopped.become_, Step::Stop(Stopped)));
1482    }
1483}