1use core::marker::PhantomData;
4
5use behavior::{
6 Actions, Address, Behavior, BirthMode, ChildChoice, ChildHead, ChildOccurrenceProduct,
7 ChildOccurrenceShape, ChildOccurrences, ChildTail, CreationId, CreationKind, EventLayer,
8 InterpreterRequests, Never, ResolveChildOccurrence, SendEffects, SendLayer, Step,
9};
10
11use super::shutdown_coordinator::heterogeneous;
12use super::{
13 HeterogeneousShutdownCoordinator, HeterogeneousShutdownPlan, NoShutdownTargets,
14 ReportShutdownPlan, ShutdownChoice, ShutdownPlanError, ShutdownTargetAt,
15};
16use crate::{CreationResolved, ObserveCreation};
17use behavior::Protocol;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum ChildCreationExpectation {
23 UnknownPosition,
25 NotRequested,
27 Awaiting {
29 creation: CreationId,
30 kind: CreationKind,
31 },
32 Established { creation: CreationId },
34 PlanReported,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
40pub enum ChildShutdownPlanError<E, A: Address> {
41 #[error("application behavior rejected the transition")]
43 Behavior(#[source] E),
44 #[error("more than one configured creation was emitted for child position {position}")]
46 DuplicateCreation { position: usize },
47 #[error("configured child position {position} was not created during initialization")]
49 MissingCreation { position: usize },
50 #[error("creation result did not match child position {position}")]
52 UnexpectedCreationResult {
53 position: usize,
54 expected: ChildCreationExpectation,
55 observed: CreationResolved<A>,
56 },
57 #[error("required child creation was rejected")]
59 CreationRejected {
60 position: usize,
61 observed: CreationResolved<A>,
62 },
63 #[error(transparent)]
65 InvalidPlan(ShutdownPlanError<CreationId>),
66 #[error("required child position {position} has not committed")]
70 ChildNotEstablished { position: usize },
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::{Activate as _, ShutdownCoordinatorError, StopOnShutdown};
77 use behavior::{
78 BehaviorActed, BehaviorBase, Births, ChildOccurrence, ChildRole, Children, CreateChild,
79 CreationSequence, Creations, DeclaredChildOccurrence, Here, Inside, MailAddr, NoBirths,
80 NoSends, User,
81 };
82
83 struct Store;
84 struct Gateway;
85
86 macro_rules! child {
87 ($child:ty) => {
88 impl Protocol for $child {
89 type Addr = MailAddr;
90 type Msg = Never;
91 }
92
93 impl Behavior for $child {
94 type Protocol = Self;
95 type Event = User<MailAddr, Never>;
96 type Sends = NoSends;
97 type Ph = Never;
98 type Error = Never;
99 type Birth = NoBirths;
100
101 fn transition(
102 &mut self,
103 _: behavior::ActiveTurn,
104 event: Self::Event,
105 ) -> BehaviorActed<Self> {
106 match event.message {}
107 }
108 }
109 };
110 }
111
112 child!(Store);
113 child!(Gateway);
114
115 type StoreChild = StopOnShutdown<Store>;
116 type GatewayChild = StopOnShutdown<Gateway>;
117 type ChildrenNode = ChildChoice<GatewayChild, ChildChoice<StoreChild, Never>>;
118
119 struct StoreRole;
120 struct GatewayRole;
121
122 #[derive(Clone, Copy)]
123 struct ApplicationChildren {
124 store: CreationId,
125 gateway: CreationId,
126 alternate_store: CreationId,
127 }
128
129 impl ApplicationChildren {
130 fn issue() -> Self {
131 let mut creations = CreationSequence::new();
132 let store = creations.issue().expect("the store creation ID exists");
133 let gateway = creations.issue().expect("the gateway creation ID exists");
134 let alternate_store = creations
135 .issue()
136 .expect("the alternate store creation ID exists");
137 Self {
138 store,
139 gateway,
140 alternate_store,
141 }
142 }
143 }
144
145 #[derive(Clone, Copy)]
146 enum InitialChildren {
147 Complete {
148 store: CreationId,
149 gateway: CreationId,
150 },
151 MissingStore {
152 gateway: CreationId,
153 },
154 DuplicateStore {
155 store: CreationId,
156 alternate_store: CreationId,
157 gateway: CreationId,
158 },
159 }
160
161 struct Application(InitialChildren);
162
163 impl Application {
164 const fn complete(children: ApplicationChildren) -> Self {
165 Self(InitialChildren::Complete {
166 store: children.store,
167 gateway: children.gateway,
168 })
169 }
170
171 const fn missing_store(children: ApplicationChildren) -> Self {
172 Self(InitialChildren::MissingStore {
173 gateway: children.gateway,
174 })
175 }
176
177 const fn duplicate_store(children: ApplicationChildren) -> Self {
178 Self(InitialChildren::DuplicateStore {
179 store: children.store,
180 alternate_store: children.alternate_store,
181 gateway: children.gateway,
182 })
183 }
184 }
185 impl Protocol for Application {
186 type Addr = MailAddr;
187 type Msg = Never;
188 }
189
190 impl BehaviorBase for Application {
191 type Base = Self;
192
193 fn base(&self) -> &Self::Base {
194 self
195 }
196 }
197
198 impl ChildRole<Application> for StoreRole {
199 type Child = StoreChild;
200 type Position = ChildTail<ChildHead>;
201 }
202
203 impl ChildOccurrence<Application> for StoreRole {
204 type Resolution = DeclaredChildOccurrence;
205 }
206
207 impl ChildRole<Application> for GatewayRole {
208 type Child = GatewayChild;
209 type Position = ChildHead;
210 }
211
212 impl ChildOccurrence<Application> for GatewayRole {
213 type Resolution = DeclaredChildOccurrence;
214 }
215
216 impl Behavior for Application {
217 type Protocol = Self;
218 type Event = User<MailAddr, Never>;
219 type Sends = NoSends;
220 type Ph = Never;
221 type Error = Never;
222 type Birth = Births<ChildrenNode>;
223
224 fn init(&mut self, _: behavior::InitializationTurn) -> BehaviorActed<Self> {
225 let creates = match self.0 {
226 InitialChildren::Complete { store, gateway } => Children::<MailAddr>::new()
227 .child(store, StopOnShutdown::new(Store))
228 .child(gateway, StopOnShutdown::new(Gateway))
229 .into_creates(),
230 InitialChildren::MissingStore { gateway } => Creations::one(CreateChild::birth(
231 gateway,
232 ChildChoice::Head(StopOnShutdown::new(Gateway)),
233 )),
234 InitialChildren::DuplicateStore {
235 store,
236 alternate_store,
237 gateway,
238 } => {
239 let store_child =
240 || ChildChoice::Tail(ChildChoice::Head(StopOnShutdown::new(Store)));
241 Creations::one(CreateChild::birth(store, store_child()))
242 .and(CreateChild::birth(alternate_store, store_child()))
243 .and(CreateChild::birth(
244 gateway,
245 ChildChoice::Head(StopOnShutdown::new(Gateway)),
246 ))
247 }
248 };
249 Ok(Actions::create(creates))
250 }
251
252 fn transition(
253 &mut self,
254 _: behavior::ActiveTurn,
255 event: Self::Event,
256 ) -> BehaviorActed<Self> {
257 match event.message {}
258 }
259 }
260
261 #[test]
262 fn rejected_creation_remains_a_typed_planning_failure() {
263 let children = ApplicationChildren::issue();
264 let mut active = shutdown_after_children(Application::complete(children))
265 .shutdown_phase(StoreRole)
266 .shutdown_phase(GatewayRole)
267 .finish()
268 .initialize()
269 .unwrap()
270 .behavior;
271 let rejected = CreationResolved::rejected(
272 children.store,
273 CreationKind::Birth,
274 behavior::CreationRejection::EnvironmentFailed,
275 );
276 let result = active.on_path::<_, Inside<Inside<Here>>>(rejected);
277 let Err(error) = result else {
278 panic!("rejected creation unexpectedly produced actions");
279 };
280 assert!(matches!(
281 error,
282 ShutdownCoordinatorError::Behavior(ChildShutdownPlanError::CreationRejected {
283 position: 1,
284 observed,
285 }) if observed == rejected
286 ));
287 }
288
289 #[test]
290 fn configured_children_are_total_and_unique_before_any_observation_is_emitted() {
291 let children = ApplicationChildren::issue();
292 let missing = shutdown_after_children(Application::missing_store(children))
293 .shutdown_phase(StoreRole)
294 .shutdown_phase(GatewayRole)
295 .finish()
296 .initialize();
297 assert!(matches!(
298 missing,
299 Err(ShutdownCoordinatorError::Behavior(
300 ChildShutdownPlanError::MissingCreation { position: 1 }
301 ))
302 ));
303
304 let duplicate = shutdown_after_children(Application::duplicate_store(children))
305 .shutdown_phase(StoreRole)
306 .shutdown_phase(GatewayRole)
307 .finish()
308 .initialize();
309 assert!(matches!(
310 duplicate,
311 Err(ShutdownCoordinatorError::Behavior(
312 ChildShutdownPlanError::DuplicateCreation { position: 1 }
313 ))
314 ));
315 }
316
317 #[test]
318 fn mismatched_and_stale_creation_resolutions_are_returned_complete() {
319 let children = ApplicationChildren::issue();
320 let mut mismatched = shutdown_after_children(Application::complete(children))
321 .shutdown_phase(StoreRole)
322 .shutdown_phase(GatewayRole)
323 .finish()
324 .initialize()
325 .unwrap()
326 .behavior;
327 let observed =
328 CreationResolved::replacement(children.alternate_store, children.store, MailAddr(191));
329 let Err(ShutdownCoordinatorError::Behavior(
330 ChildShutdownPlanError::UnexpectedCreationResult {
331 position,
332 expected,
333 observed: returned,
334 },
335 )) = mismatched.on_path::<_, Inside<Inside<Here>>>(observed)
336 else {
337 panic!("mismatched fact was not returned through the typed failure");
338 };
339 assert_eq!(position, 1);
340 assert_eq!(
341 expected,
342 ChildCreationExpectation::Awaiting {
343 creation: children.store,
344 kind: CreationKind::Birth,
345 }
346 );
347 assert_eq!(returned, observed);
348
349 let mut stale = shutdown_after_children(Application::complete(children))
350 .shutdown_phase(StoreRole)
351 .shutdown_phase(GatewayRole)
352 .finish()
353 .initialize()
354 .unwrap()
355 .behavior;
356 assert!(
357 stale
358 .on_path::<_, Inside<Inside<Here>>>(CreationResolved::birth(
359 children.store,
360 MailAddr(101),
361 ))
362 .is_ok()
363 );
364 assert!(
365 stale
366 .on_path::<_, Inside<Here>>(CreationResolved::birth(
367 children.gateway,
368 MailAddr(102),
369 ))
370 .is_ok()
371 );
372 let observed = CreationResolved::birth(children.gateway, MailAddr(202));
373 let Err(ShutdownCoordinatorError::Behavior(
374 ChildShutdownPlanError::UnexpectedCreationResult {
375 position,
376 expected,
377 observed: returned,
378 },
379 )) = stale.on_path::<_, Inside<Here>>(observed)
380 else {
381 panic!("stale fact was silently discarded");
382 };
383 assert_eq!(position, 0);
384 assert_eq!(expected, ChildCreationExpectation::PlanReported);
385 assert_eq!(returned, observed);
386 }
387
388 #[test]
389 fn creation_resolution_requires_matching_id_and_kind_independently() {
390 let children = ApplicationChildren::issue();
391 let wrong_kind =
392 CreationResolved::replacement(children.store, children.gateway, MailAddr(191));
393 let wrong_id = CreationResolved::birth(children.alternate_store, MailAddr(192));
394
395 for observed in [wrong_kind, wrong_id] {
396 let mut active = shutdown_after_children(Application::complete(children))
397 .shutdown_phase(StoreRole)
398 .shutdown_phase(GatewayRole)
399 .finish()
400 .initialize()
401 .unwrap()
402 .behavior;
403 let Err(ShutdownCoordinatorError::Behavior(
404 ChildShutdownPlanError::UnexpectedCreationResult {
405 position,
406 expected,
407 observed: returned,
408 },
409 )) = active.on_path::<_, Inside<Inside<Here>>>(observed)
410 else {
411 panic!("a mismatched creation component must return the report");
412 };
413 assert_eq!(position, 1);
414 assert_eq!(
415 expected,
416 ChildCreationExpectation::Awaiting {
417 creation: children.store,
418 kind: CreationKind::Birth,
419 }
420 );
421 assert_eq!(returned, observed);
422
423 let Ok(accepted) = active.on_path::<_, Inside<Inside<Here>>>(CreationResolved::birth(
424 children.store,
425 MailAddr(193),
426 )) else {
427 panic!("the exact birth remains admissible after rejection");
428 };
429 assert!(accepted.creates.is_empty());
430 assert!(matches!(accepted.become_, Step::Continue));
431 }
432 }
433}
434
435#[must_use]
441pub fn shutdown_after_children<B>(
442 application: B,
443) -> ChildShutdownPhases<B, AvailableFor<B>, NoPhases>
444where
445 B: Behavior,
446 <B::Birth as BirthMode>::Child: ChildOccurrenceProduct<AvailableChildren>,
447{
448 ChildShutdownPhases {
449 application,
450 available: PhantomData,
451 phases: PhantomData,
452 }
453}
454
455pub struct ChildShutdownPhases<B, Available, Phases> {
457 application: B,
458 available: PhantomData<fn() -> Available>,
459 phases: PhantomData<fn() -> Phases>,
460}
461
462pub trait BeginShutdownPhases: Behavior + Sized {
469 type Output;
470
471 #[must_use]
472 fn begin_shutdown_phases(self) -> Self::Output;
473}
474
475impl<B> BeginShutdownPhases for B
476where
477 B: Behavior,
478 <B::Birth as BirthMode>::Child: ChildOccurrenceProduct<AvailableChildren>,
479{
480 type Output = ChildShutdownPhases<B, AvailableFor<B>, NoPhases>;
481
482 fn begin_shutdown_phases(self) -> Self::Output {
483 shutdown_after_children(self)
484 }
485}
486
487pub trait DeclareShutdownPhase<Role>: Sized {
493 type Output;
494
495 #[must_use]
496 fn shutdown_phase(self, role: Role) -> Self::Output;
497}
498
499pub trait FinishShutdownPhases: Sized {
506 type Output;
507
508 #[must_use]
509 fn finish(self) -> Self::Output;
510}
511
512#[allow(
513 private_bounds,
514 reason = "the public inferred builder hides its closed type-level phase proof"
515)]
516impl<B, Available, Phases> ChildShutdownPhases<B, Available, Phases>
517where
518 B: Behavior,
519{
520 #[must_use]
601 pub fn shutdown_phase<Role>(
602 self,
603 _: Role,
604 ) -> ChildShutdownPhases<
605 B,
606 <Available as AssignAt<<B as ResolveChildOccurrence<Role>>::Position>>::Assigned,
607 Phase<Role, Phases>,
608 >
609 where
610 B: ResolveChildOccurrence<Role>,
611 Available: AssignAt<<B as ResolveChildOccurrence<Role>>::Position>,
612 {
613 ChildShutdownPhases {
614 application: self.application,
615 available: PhantomData,
616 phases: PhantomData,
617 }
618 }
619}
620
621#[allow(
622 private_bounds,
623 reason = "the public consumer operation preserves the builder's closed availability proof"
624)]
625impl<B, Available, Phases, Role> DeclareShutdownPhase<Role>
626 for ChildShutdownPhases<B, Available, Phases>
627where
628 B: Behavior + ResolveChildOccurrence<Role>,
629 Available: AssignAt<<B as ResolveChildOccurrence<Role>>::Position>,
630{
631 type Output = ChildShutdownPhases<
632 B,
633 <Available as AssignAt<<B as ResolveChildOccurrence<Role>>::Position>>::Assigned,
634 Phase<Role, Phases>,
635 >;
636
637 fn shutdown_phase(self, role: Role) -> Self::Output {
638 ChildShutdownPhases::shutdown_phase(self, role)
639 }
640}
641
642#[allow(
643 private_bounds,
644 reason = "the public inferred builder hides its closed plan-construction proof"
645)]
646impl<B, Available, Phases> ChildShutdownPhases<B, Available, Phases>
647where
648 B: Behavior,
649 B::Birth: BirthMode,
650 Available: AllAssigned,
651 <B::Birth as BirthMode>::Child: ChildOccurrenceProduct<ShutdownTargets<B>>,
652 Phases: BuildShutdownPlan<B, TargetsFor<B>>,
653 TargetsFor<B>: super::shutdown_coordinator::heterogeneous::Selection<Addr = behavior::BehaviorAddr<B>>
654 + Copy,
655 <behavior::BehaviorAddr<B> as Address>::Nonce: Copy + Eq,
656{
657 #[must_use]
688 pub fn finish(
689 self,
690 ) -> HeterogeneousShutdownCoordinator<ChildShutdownPlan<B, Phases>, TargetsFor<B>>
691 where
692 ChildShutdownPlan<B, Phases>: Behavior<Protocol = B::Protocol>,
693 {
694 HeterogeneousShutdownCoordinator::awaiting_plan(ChildShutdownPlan::new(self.application))
695 }
696}
697
698#[allow(
699 private_bounds,
700 reason = "the public consumer operation preserves the builder's closed finishing proof"
701)]
702impl<B, Available, Phases> FinishShutdownPhases for ChildShutdownPhases<B, Available, Phases>
703where
704 B: Behavior,
705 B::Birth: BirthMode,
706 Available: AllAssigned,
707 <B::Birth as BirthMode>::Child: ChildOccurrenceProduct<ShutdownTargets<B>>,
708 Phases: BuildShutdownPlan<B, TargetsFor<B>>,
709 TargetsFor<B>: super::shutdown_coordinator::heterogeneous::Selection<Addr = behavior::BehaviorAddr<B>>
710 + Copy,
711 <behavior::BehaviorAddr<B> as Address>::Nonce: Copy + Eq,
712 ChildShutdownPlan<B, Phases>: Behavior<Protocol = B::Protocol>,
713{
714 type Output = HeterogeneousShutdownCoordinator<ChildShutdownPlan<B, Phases>, TargetsFor<B>>;
715
716 fn finish(self) -> Self::Output {
717 ChildShutdownPhases::finish(self)
718 }
719}
720
721pub struct NoPhases;
723
724pub struct Phase<Role, Earlier>(PhantomData<fn() -> (Role, Earlier)>);
726
727pub struct AvailableChildren;
729
730pub struct Unassigned<Child, Tail>(PhantomData<fn() -> (Child, Tail)>);
732
733pub struct Assigned<Child, Tail>(PhantomData<fn() -> (Child, Tail)>);
735
736pub struct NoChildren;
738
739impl ChildOccurrenceShape for AvailableChildren {
740 type Empty = NoChildren;
741 type Member<Occurrence, Child: Behavior, Tail> = Unassigned<Child, Tail>;
742}
743
744pub trait AssignAt<Position> {
746 type Assigned;
747}
748
749impl<Child, Tail> AssignAt<ChildHead> for Unassigned<Child, Tail> {
750 type Assigned = Assigned<Child, Tail>;
751}
752
753impl<Child, Tail, Position> AssignAt<ChildTail<Position>> for Unassigned<Child, Tail>
754where
755 Tail: AssignAt<Position>,
756{
757 type Assigned = Unassigned<Child, <Tail as AssignAt<Position>>::Assigned>;
758}
759
760impl<Child, Tail, Position> AssignAt<ChildTail<Position>> for Assigned<Child, Tail>
761where
762 Tail: AssignAt<Position>,
763{
764 type Assigned = Assigned<Child, <Tail as AssignAt<Position>>::Assigned>;
765}
766
767pub trait AllAssigned {}
769
770impl AllAssigned for NoChildren {}
771
772impl<Child, Tail: AllAssigned> AllAssigned for Assigned<Child, Tail> {}
773
774pub struct ShutdownTargets<B: Behavior>(PhantomData<fn() -> B>);
776
777impl<B: Behavior> ChildOccurrenceShape for ShutdownTargets<B> {
778 type Empty = NoShutdownTargets<behavior::BehaviorAddr<B>>;
779 type Member<Occurrence, Child: Behavior, Tail> = ShutdownChoice<Child, Tail>;
780}
781
782pub type TargetsFor<B> =
784 ChildOccurrences<<<B as Behavior>::Birth as BirthMode>::Child, ShutdownTargets<B>>;
785
786pub type AvailableFor<B> =
788 ChildOccurrences<<<B as Behavior>::Birth as BirthMode>::Child, AvailableChildren>;
789
790trait PositionNumber {
791 const INDEX: usize;
792}
793
794impl PositionNumber for ChildHead {
795 const INDEX: usize = 0;
796}
797
798impl<Position: PositionNumber> PositionNumber for ChildTail<Position> {
799 const INDEX: usize = Position::INDEX + 1;
800}
801
802#[derive(Debug, Clone, Copy, PartialEq, Eq)]
803enum ChildStatus {
804 NotRequested,
805 Awaiting {
806 creation: CreationId,
807 kind: CreationKind,
808 },
809 Established {
810 creation: CreationId,
811 },
812}
813
814impl ChildStatus {
815 fn expectation(self) -> ChildCreationExpectation {
816 match self {
817 Self::NotRequested => ChildCreationExpectation::NotRequested,
818 Self::Awaiting { creation, kind } => {
819 ChildCreationExpectation::Awaiting { creation, kind }
820 }
821 Self::Established { creation } => ChildCreationExpectation::Established { creation },
822 }
823 }
824}
825
826enum Planning {
827 Collecting(Vec<ChildStatus>),
828 Reported,
829}
830
831enum PlannedEvent<E, A: Address> {
832 Application(E),
833 Creation {
834 position: usize,
835 result: CreationResolved<A>,
836 },
837}
838
839pub struct ChildShutdownPlan<B: Behavior, Phases> {
842 application: B,
843 planning: Planning,
844 phases: PhantomData<fn() -> Phases>,
845}
846
847impl<B: Behavior, Phases> ChildShutdownPlan<B, Phases> {
848 fn new(application: B) -> Self {
849 Self {
850 application,
851 planning: Planning::Collecting(Vec::new()),
852 phases: PhantomData,
853 }
854 }
855}
856
857impl<B, Phases> behavior::BehaviorBase for ChildShutdownPlan<B, Phases>
858where
859 B: Behavior + behavior::BehaviorBase,
860{
861 type Base = B::Base;
862
863 fn base(&self) -> &Self::Base {
864 self.application.base()
865 }
866}
867
868impl<B, Phases> crate::StashStatus for ChildShutdownPlan<B, Phases>
869where
870 B: Behavior + crate::StashStatus,
871{
872 fn stashed_messages(&self) -> usize {
873 self.application.stashed_messages()
874 }
875}
876
877type Plan<Targets> = HeterogeneousShutdownPlan<Targets>;
878type ReportPlan<Targets> = ReportShutdownPlan<Plan<Targets>>;
879type BaseSends<B, Targets> =
880 SendLayer<InterpreterRequests<ReportPlan<Targets>>, <B as Behavior>::Sends>;
881
882struct PlanningChildren;
883struct PlannedEnd;
884struct PlannedChild<Position, Child, Tail>(PhantomData<fn() -> (Position, Child, Tail)>);
885
886impl ChildOccurrenceShape for PlanningChildren {
887 type Empty = PlannedEnd;
888 type Member<Occurrence, Child: Behavior, Tail> = PlannedChild<Occurrence, Child, Tail>;
889}
890
891trait PlanChildren<Node, A: Address, Event, Sends> {
892 type Events;
893 type Sends: SendEffects;
894
895 const COUNT: usize;
896
897 fn empty_sends(inner: Sends) -> Self::Sends;
898
899 fn observe(
900 child: &Node,
901 creation: CreationId,
902 kind: CreationKind,
903 sends: &mut Self::Sends,
904 requested: &mut Vec<(usize, CreationId, CreationKind)>,
905 );
906
907 fn read(event: Self::Events) -> PlannedEvent<Event, A>;
908}
909
910impl<A, Event, Sends> PlanChildren<Never, A, Event, Sends> for PlannedEnd
911where
912 A: Address,
913 Sends: SendEffects,
914{
915 type Events = EventLayer<Never, Event>;
916 type Sends = Sends;
917 const COUNT: usize = 0;
918
919 fn empty_sends(inner: Sends) -> Self::Sends {
920 inner
921 }
922
923 fn observe(
924 child: &Never,
925 _: CreationId,
926 _: CreationKind,
927 _: &mut Self::Sends,
928 _: &mut Vec<(usize, CreationId, CreationKind)>,
929 ) {
930 match *child {}
931 }
932
933 fn read(event: Self::Events) -> PlannedEvent<Event, A> {
934 match event {
935 EventLayer::Owned(never) => match never {},
936 EventLayer::Inner(event) => PlannedEvent::Application(event),
937 }
938 }
939}
940
941impl<A, Position, Event, Sends, Child> PlanChildren<Child, A, Event, Sends>
942 for PlannedChild<Position, Child, PlannedEnd>
943where
944 A: Address,
945 Position: PositionNumber,
946 Sends: SendEffects,
947 Child: Behavior,
948 Child::Protocol: Protocol<Addr = A>,
949{
950 type Events = EventLayer<CreationResolved<A>, EventLayer<Never, Event>>;
951 type Sends = SendLayer<InterpreterRequests<ObserveCreation<Child::Protocol, Position>>, Sends>;
952 const COUNT: usize = 1;
953
954 fn empty_sends(inner: Sends) -> Self::Sends {
955 SendLayer::new(InterpreterRequests::empty(), inner)
956 }
957
958 fn observe(
959 _: &Child,
960 creation: CreationId,
961 kind: CreationKind,
962 sends: &mut Self::Sends,
963 requested: &mut Vec<(usize, CreationId, CreationKind)>,
964 ) {
965 sends.owned.send(ObserveCreation::new(creation));
966 requested.push((Position::INDEX, creation, kind));
967 }
968
969 fn read(event: Self::Events) -> PlannedEvent<Event, A> {
970 match event {
971 EventLayer::Owned(result) => PlannedEvent::Creation {
972 position: Position::INDEX,
973 result,
974 },
975 EventLayer::Inner(EventLayer::Owned(never)) => match never {},
976 EventLayer::Inner(EventLayer::Inner(event)) => PlannedEvent::Application(event),
977 }
978 }
979}
980
981impl<A, Position, Event, Sends, Head, Tail, TailPlan>
982 PlanChildren<ChildChoice<Head, Tail>, A, Event, Sends>
983 for PlannedChild<Position, Head, TailPlan>
984where
985 A: Address,
986 Position: PositionNumber,
987 Sends: SendEffects,
988 Head: Behavior,
989 Head::Protocol: Protocol<Addr = A>,
990 TailPlan: PlanChildren<Tail, A, Event, Sends>,
991{
992 type Events = EventLayer<CreationResolved<A>, TailPlan::Events>;
993 type Sends =
994 SendLayer<InterpreterRequests<ObserveCreation<Head::Protocol, Position>>, TailPlan::Sends>;
995
996 const COUNT: usize = 1 + TailPlan::COUNT;
997
998 fn empty_sends(inner: Sends) -> Self::Sends {
999 SendLayer::new(InterpreterRequests::empty(), TailPlan::empty_sends(inner))
1000 }
1001
1002 fn observe(
1003 child: &ChildChoice<Head, Tail>,
1004 creation: CreationId,
1005 kind: CreationKind,
1006 sends: &mut Self::Sends,
1007 requested: &mut Vec<(usize, CreationId, CreationKind)>,
1008 ) {
1009 match child {
1010 ChildChoice::Head(_) => {
1011 sends.owned.send(ObserveCreation::new(creation));
1012 requested.push((Position::INDEX, creation, kind));
1013 }
1014 ChildChoice::Tail(tail) => {
1015 TailPlan::observe(tail, creation, kind, &mut sends.inner, requested);
1016 }
1017 }
1018 }
1019
1020 fn read(event: Self::Events) -> PlannedEvent<Event, A> {
1021 match event {
1022 EventLayer::Owned(result) => PlannedEvent::Creation {
1023 position: Position::INDEX,
1024 result,
1025 },
1026 EventLayer::Inner(event) => TailPlan::read(event),
1027 }
1028 }
1029}
1030
1031trait BuildShutdownPlan<B: Behavior, Targets> {
1032 fn build(
1033 children: &[ChildStatus],
1034 ) -> Result<Vec<Vec<Targets>>, ChildShutdownPlanError<B::Error, behavior::BehaviorAddr<B>>>;
1035}
1036
1037impl<B: Behavior, Targets> BuildShutdownPlan<B, Targets> for NoPhases {
1038 fn build(
1039 _: &[ChildStatus],
1040 ) -> Result<Vec<Vec<Targets>>, ChildShutdownPlanError<B::Error, behavior::BehaviorAddr<B>>>
1041 {
1042 Ok(Vec::new())
1043 }
1044}
1045
1046impl<B, Targets, Role, Earlier> BuildShutdownPlan<B, Targets> for Phase<Role, Earlier>
1047where
1048 B: Behavior + ResolveChildOccurrence<Role>,
1049 behavior::BehaviorAddr<B>: Address,
1050 <B as ResolveChildOccurrence<Role>>::Position: PositionNumber,
1051 <<B as ResolveChildOccurrence<Role>>::Child as Behavior>::Protocol:
1052 Protocol<Addr = behavior::BehaviorAddr<B>>,
1053 Targets: ShutdownTargetAt<
1054 <B as ResolveChildOccurrence<Role>>::Child,
1055 <B as ResolveChildOccurrence<Role>>::Position,
1056 >,
1057 Earlier: BuildShutdownPlan<B, Targets>,
1058{
1059 fn build(
1060 children: &[ChildStatus],
1061 ) -> Result<Vec<Vec<Targets>>, ChildShutdownPlanError<B::Error, behavior::BehaviorAddr<B>>>
1062 {
1063 let mut phases = Earlier::build(children)?;
1064 let position = <<B as ResolveChildOccurrence<Role>>::Position as PositionNumber>::INDEX;
1065 let Some(ChildStatus::Established { creation }) = children.get(position) else {
1066 return Err(ChildShutdownPlanError::ChildNotEstablished { position });
1067 };
1068 phases.push(vec![Targets::shutdown_target_at(*creation)]);
1069 Ok(phases)
1070 }
1071}
1072
1073impl<B, Phases, A, Ph, Sends, Br, Node, Shape, Targets, Events, Planned> Behavior
1074 for ChildShutdownPlan<B, Phases>
1075where
1076 A: Address,
1077 A::Nonce: Copy + Eq,
1078 Sends: SendEffects,
1079 Br: BirthMode<Child = Node>,
1080 B: Behavior<Ph = Ph, Sends = Sends, Birth = Br>,
1081 B::Protocol: Protocol<Addr = A>,
1082 Node: ChildOccurrenceProduct<ShutdownTargets<B>, Product = Targets>
1083 + ChildOccurrenceProduct<PlanningChildren, Product = Shape>,
1084 Shape: PlanChildren<Node, A, B::Event, BaseSends<B, Targets>, Events = Events, Sends = Planned>,
1085 Targets: heterogeneous::Selection<Addr = A> + Copy,
1086 Phases: BuildShutdownPlan<B, Targets>,
1087 Events: behavior::UserEvent<Addr = A, Message = <B::Protocol as Protocol>::Msg>,
1088 Planned: SendEffects + behavior::SendsFor<Events>,
1089{
1090 type Protocol = B::Protocol;
1091 type Event = Events;
1092 type Sends = Planned;
1093 type Ph = Ph;
1094 type Error = ChildShutdownPlanError<B::Error, A>;
1095 type Birth = Br;
1096
1097 fn init(&mut self, _: behavior::InitializationTurn) -> behavior::BehaviorActed<Self> {
1098 let actions = behavior::initialize(&mut self.application)
1099 .map_err(ChildShutdownPlanError::Behavior)?;
1100 self.wrap_initialization(actions)
1101 }
1102
1103 fn transition(
1104 &mut self,
1105 _: behavior::ActiveTurn,
1106 event: Self::Event,
1107 ) -> behavior::BehaviorActed<Self> {
1108 match Shape::read(event) {
1109 PlannedEvent::Application(event) => {
1110 let actions = behavior::delegate_transition(&mut self.application, event)
1111 .map_err(ChildShutdownPlanError::Behavior)?;
1112 Ok(self.wrap_transition(actions))
1113 }
1114 PlannedEvent::Creation { position, result } => self.creation_resolved(position, result),
1115 }
1116 }
1117}
1118
1119#[allow(
1120 private_bounds,
1121 reason = "the public compiler representation hides its closed planning fold"
1122)]
1123impl<B, Phases, A, Ph, Sends, Br, Node, Shape, Targets, Events, Planned>
1124 ChildShutdownPlan<B, Phases>
1125where
1126 A: Address,
1127 A::Nonce: Copy + Eq,
1128 Sends: SendEffects,
1129 Br: BirthMode<Child = Node>,
1130 B: Behavior<Ph = Ph, Sends = Sends, Birth = Br>,
1131 B::Protocol: Protocol<Addr = A>,
1132 Node: ChildOccurrenceProduct<ShutdownTargets<B>, Product = Targets>
1133 + ChildOccurrenceProduct<PlanningChildren, Product = Shape>,
1134 Shape: PlanChildren<Node, A, B::Event, BaseSends<B, Targets>, Events = Events, Sends = Planned>,
1135 Targets: heterogeneous::Selection<Addr = A> + Copy,
1136 Phases: BuildShutdownPlan<B, Targets>,
1137 Events: behavior::UserEvent<Addr = A, Message = <B::Protocol as Protocol>::Msg>,
1138 Planned: SendEffects + behavior::SendsFor<Events>,
1139{
1140 fn wrap_initialization(
1141 &mut self,
1142 actions: Actions<A, Ph, Sends, Br>,
1143 ) -> Result<Actions<A, Ph, Planned, Br>, ChildShutdownPlanError<B::Error, A>> {
1144 let Actions {
1145 sends,
1146 creates,
1147 become_,
1148 } = actions;
1149 let reports = if Shape::COUNT == 0 {
1150 let phases = Phases::build(&[])?;
1151 let plan = HeterogeneousShutdownPlan::new(phases)
1152 .map_err(ChildShutdownPlanError::InvalidPlan)?;
1153 self.planning = Planning::Reported;
1154 InterpreterRequests::one(crate::ReportShutdownPlan::new(plan))
1155 } else {
1156 InterpreterRequests::empty()
1157 };
1158 let base = SendLayer::new(reports, sends);
1159 let mut planned = Shape::empty_sends(base);
1160 let mut requested = Vec::new();
1161 for creation in &creates {
1162 Shape::observe(
1163 creation.child(),
1164 creation.id(),
1165 creation.kind(),
1166 &mut planned,
1167 &mut requested,
1168 );
1169 }
1170 if Shape::COUNT != 0 {
1171 let mut next = vec![ChildStatus::NotRequested; Shape::COUNT];
1172 for (position, creation, kind) in requested {
1173 let Some(state) = next.get_mut(position) else {
1174 return Err(ChildShutdownPlanError::DuplicateCreation { position });
1175 };
1176 if !matches!(state, ChildStatus::NotRequested) {
1177 return Err(ChildShutdownPlanError::DuplicateCreation { position });
1178 }
1179 *state = ChildStatus::Awaiting { creation, kind };
1180 }
1181 if let Some(position) = next
1182 .iter()
1183 .position(|child| matches!(child, ChildStatus::NotRequested))
1184 {
1185 return Err(ChildShutdownPlanError::MissingCreation { position });
1186 }
1187 self.planning = Planning::Collecting(next);
1188 }
1189 Ok(Actions::new(planned, creates, become_))
1190 }
1191
1192 fn wrap_transition(&self, actions: Actions<A, Ph, Sends, Br>) -> Actions<A, Ph, Planned, Br> {
1193 let Actions {
1194 sends,
1195 creates,
1196 become_,
1197 } = actions;
1198 let base = SendLayer::new(InterpreterRequests::empty(), sends);
1199 Actions::new(Shape::empty_sends(base), creates, become_)
1200 }
1201
1202 fn creation_resolved(
1203 &mut self,
1204 position: usize,
1205 result: CreationResolved<A>,
1206 ) -> Result<Actions<A, Ph, Planned, Br>, ChildShutdownPlanError<B::Error, A>> {
1207 let Planning::Collecting(children) = &self.planning else {
1208 return Err(ChildShutdownPlanError::UnexpectedCreationResult {
1209 position,
1210 expected: ChildCreationExpectation::PlanReported,
1211 observed: result,
1212 });
1213 };
1214 let Some(state) = children.get(position).copied() else {
1215 return Err(ChildShutdownPlanError::UnexpectedCreationResult {
1216 position,
1217 expected: ChildCreationExpectation::UnknownPosition,
1218 observed: result,
1219 });
1220 };
1221 let ChildStatus::Awaiting { creation, kind } = state else {
1222 return Err(ChildShutdownPlanError::UnexpectedCreationResult {
1223 position,
1224 expected: state.expectation(),
1225 observed: result,
1226 });
1227 };
1228 if result.creation != creation || result.kind != kind {
1229 return Err(ChildShutdownPlanError::UnexpectedCreationResult {
1230 position,
1231 expected: ChildCreationExpectation::Awaiting { creation, kind },
1232 observed: result,
1233 });
1234 }
1235 let established = match result.result {
1236 Ok(_) => ChildStatus::Established { creation },
1237 Err(_) => {
1238 return Err(ChildShutdownPlanError::CreationRejected {
1239 position,
1240 observed: result,
1241 });
1242 }
1243 };
1244 let mut next = children.clone();
1245 next[position] = established;
1246 if !next
1247 .iter()
1248 .all(|child| matches!(child, ChildStatus::Established { .. }))
1249 {
1250 self.planning = Planning::Collecting(next);
1251 return Ok(Actions::cont());
1252 }
1253 let phases = Phases::build(&next)?;
1254 let plan =
1255 HeterogeneousShutdownPlan::new(phases).map_err(ChildShutdownPlanError::InvalidPlan)?;
1256 let base = SendLayer::new(
1257 InterpreterRequests::one(crate::ReportShutdownPlan::new(plan)),
1258 B::Sends::empty(),
1259 );
1260 let sends = Shape::empty_sends(base);
1261 self.planning = Planning::Reported;
1262 Ok(Actions::new(
1263 sends,
1264 behavior::Creations::empty(),
1265 Step::Continue,
1266 ))
1267 }
1268}