1use crate::{AsStopReason, Child, Complete, Execution, Outcome, PhaseSpec, PlanError, Pulse};
3use alloc::vec::Vec;
4
5#[must_use = "run phases and hand over the final result; Drop records abandonment"]
10pub struct Phases<'a> {
11 parent: &'a dyn Pulse,
12 children: Vec<Option<Child<'a>>>,
13}
14impl<'a> Phases<'a> {
15 #[track_caller]
17 pub fn new(parent: &'a dyn Pulse, execution: Execution, parts: &[PhaseSpec<'_>]) -> Self {
18 Self {
19 parent,
20 children: parent
21 .plan(execution, parts)
22 .into_iter()
23 .map(Some)
24 .collect(),
25 }
26 }
27 #[track_caller]
30 pub fn run<T, E>(
31 &mut self,
32 index: usize,
33 work: impl FnOnce(&dyn Pulse) -> Result<T, E>,
34 ) -> Result<T, E>
35 where
36 E: AsStopReason,
37 {
38 let child = self.begin(index);
39 let result = work(&child);
40 child.complete(result)
41 }
42 #[track_caller]
45 pub fn run_classified<T, E>(
46 &mut self,
47 index: usize,
48 is_stop: impl FnOnce(&E) -> bool,
49 work: impl FnOnce(&dyn Pulse) -> Result<T, E>,
50 ) -> Result<T, E> {
51 let child = self.begin(index);
52 let result = work(&child);
53 child.complete_classified(result, is_stop)
54 }
55 #[track_caller]
57 pub fn skip(&mut self, index: usize) {
58 self.take(index).complete_as(Outcome::Skipped);
59 }
60
61 #[track_caller]
62 fn begin(&mut self, index: usize) -> Child<'a> {
63 let child = self.take(index);
64 if let Err(error) = child.start() {
65 self.parent.record_issue(error);
66 }
67 child
68 }
69
70 #[track_caller]
71 fn take(&mut self, index: usize) -> Child<'a> {
72 if let Some(Some(child)) = self.children.get_mut(index).map(Option::take) {
73 return child;
74 }
75 self.parent.record_issue(PlanError::NoMoreStages);
76 Child::untracked(self.parent)
77 }
78}
79impl Complete for Phases<'_> {
80 fn complete_as(self, outcome: Outcome) {
81 let untouched = match outcome {
82 Outcome::Succeeded | Outcome::Skipped => Outcome::Skipped,
83 Outcome::Abandoned => Outcome::Abandoned,
84 _ => Outcome::NotRun,
85 };
86 for child in self.children.into_iter().flatten() {
87 child.complete_inferred(untouched);
88 }
89 }
90}