Skip to main content

made_core/entities/ceremony_instance/
step_execution.rs

1use crate::entities::ceremony_commands::{ApplyStepResult, StartStep};
2use crate::entities::CeremonyCommand;
3use crate::value_objects::{MaxParallel, StepClaimFence};
4
5use super::{
6    CeremonyDefinition, CeremonyEvent, CeremonyInstance, DomainError, OffsetDateTime, RoleId,
7    StepAttempt, StepId, StepLease, StepResult,
8};
9
10/// The step mutators, as wrappers over [`CeremonyInstance::decide`]
11/// and [`CeremonyInstance::apply`]: each decides its command, folds
12/// the events, and returns what its callers always got.
13impl CeremonyInstance {
14    pub fn start_step_as(
15        &mut self,
16        definition: &CeremonyDefinition,
17        role_id: &RoleId,
18        step_id: &StepId,
19        lease: StepLease,
20        now: OffsetDateTime,
21    ) -> Result<StepAttempt, DomainError> {
22        self.start_step_with(
23            definition,
24            Some(role_id.clone()),
25            step_id,
26            lease,
27            now,
28            MaxParallel::SERVER_MAX,
29        )
30    }
31
32    pub fn start_step(
33        &mut self,
34        definition: &CeremonyDefinition,
35        step_id: &StepId,
36        lease: StepLease,
37        now: OffsetDateTime,
38    ) -> Result<StepAttempt, DomainError> {
39        self.start_step_with(
40            definition,
41            None,
42            step_id,
43            lease,
44            now,
45            MaxParallel::SERVER_MAX,
46        )
47    }
48
49    fn start_step_with(
50        &mut self,
51        definition: &CeremonyDefinition,
52        role_id: Option<RoleId>,
53        step_id: &StepId,
54        lease: StepLease,
55        now: OffsetDateTime,
56        max_parallel_ceiling: MaxParallel,
57    ) -> Result<StepAttempt, DomainError> {
58        let command = CeremonyCommand::StartStep(StartStep {
59            role_id,
60            step_id: step_id.clone(),
61            lease,
62            now,
63            max_parallel_ceiling,
64        });
65        let events = self.decide(&command, definition)?;
66        let attempt = events
67            .iter()
68            .find_map(|event| match event {
69                CeremonyEvent::StepStarted(started) => Some(started.attempt),
70                _ => None,
71            })
72            .ok_or(DomainError::InvariantViolated {
73                reason: "starting a step decides a step start",
74            })?;
75        self.apply_all(&events);
76        Ok(attempt)
77    }
78
79    /// Capture this identity from the accepted claim before starting work.
80    pub fn step_claim_fence(&self, step_id: &StepId) -> Result<StepClaimFence, DomainError> {
81        let record = self.step_record(step_id).ok_or(DomainError::NotFound {
82            what: "ceremony_instance.step_record",
83        })?;
84        StepClaimFence::for_record(self.id(), step_id, record)
85    }
86
87    pub(super) fn require_step_claim_fence(
88        &self,
89        step_id: &StepId,
90        fence: &StepClaimFence,
91    ) -> Result<(), DomainError> {
92        if &self.step_claim_fence(step_id)? != fence {
93            return Err(DomainError::InvariantViolated {
94                reason: "step completion claim fence does not match the current claim",
95            });
96        }
97        Ok(())
98    }
99
100    pub fn apply_step_result(
101        &mut self,
102        definition: &CeremonyDefinition,
103        step_id: &StepId,
104        claim_fence: StepClaimFence,
105        result: StepResult,
106        now: OffsetDateTime,
107    ) -> Result<(), DomainError> {
108        let command = CeremonyCommand::ApplyStepResult(ApplyStepResult {
109            step_id: step_id.clone(),
110            claim_fence,
111            result,
112            now,
113        });
114        let events = self.decide(&command, definition)?;
115        self.apply_all(&events);
116        Ok(())
117    }
118}