made_core/entities/ceremony_definition/
guards.rs1use std::collections::BTreeMap;
2
3use crate::value_objects::{
4 CeremonyContext, CeremonyGuard, CeremonyStep, CeremonyTransition, GuardCondition, StateId,
5 StepExecutionRecord, StepId,
6};
7
8use super::CeremonyDefinition;
9
10impl CeremonyDefinition {
11 #[must_use]
12 pub fn guards_are_satisfied(
13 &self,
14 transition: &CeremonyTransition,
15 records: &BTreeMap<StepId, StepExecutionRecord>,
16 context: &CeremonyContext,
17 ) -> bool {
18 self.repeat_requirements_are_satisfied_for_transition(transition, records)
19 && transition.required_guards().iter().all(|guard_name| {
20 self.guards.get(guard_name).is_some_and(|guard| {
21 self.guard_is_satisfied_for_transition(guard, transition, records, context)
22 })
23 })
24 }
25
26 #[must_use]
33 pub fn guard_is_satisfied_for_transition(
34 &self,
35 guard: &CeremonyGuard,
36 transition: &CeremonyTransition,
37 records: &BTreeMap<StepId, StepExecutionRecord>,
38 context: &CeremonyContext,
39 ) -> bool {
40 match guard.condition() {
41 GuardCondition::StepRepeatExhausted(condition) => self
42 .step_repeat_is_exhausted_on_transition(transition, condition.step_id(), records),
43 GuardCondition::StepStatus { step_id, status } if status.is_success() => {
44 guard.is_satisfied(records, context)
45 && self.repeat_requirement_is_satisfied_or_waived(transition, step_id, records)
46 }
47 GuardCondition::AllStepsCompleted => {
48 guard.is_satisfied(records, context)
49 && self.steps.keys().all(|step_id| {
50 self.repeat_requirement_is_satisfied_or_waived(transition, step_id, records)
51 })
52 }
53 GuardCondition::AnyStepCompleted => {
54 self.steps_for_state(transition.from()).any(|step| {
55 records
56 .get(step.id())
57 .is_some_and(|record| record.status().is_success())
58 })
59 }
60 GuardCondition::StepsCompleted(required) => {
61 self.steps_for_state(transition.from())
62 .filter(|step| {
63 records
64 .get(step.id())
65 .is_some_and(|record| record.status().is_success())
66 })
67 .count()
68 >= required.get() as usize
69 }
70 _ => guard.is_satisfied(records, context),
71 }
72 }
73
74 #[must_use]
77 pub fn repeat_requirements_are_satisfied(
78 &self,
79 state_id: &StateId,
80 records: &BTreeMap<StepId, StepExecutionRecord>,
81 ) -> bool {
82 self.steps_for_state(state_id)
83 .all(|step| self.repeat_requirement_is_satisfied(step.id(), records))
84 }
85
86 #[must_use]
89 pub fn repeat_requirements_are_satisfied_for_transition(
90 &self,
91 transition: &CeremonyTransition,
92 records: &BTreeMap<StepId, StepExecutionRecord>,
93 ) -> bool {
94 self.steps_for_state(transition.from()).all(|step| {
95 self.repeat_requirement_is_satisfied_or_waived(transition, step.id(), records)
96 })
97 }
98
99 fn repeat_requirement_is_satisfied_or_waived(
100 &self,
101 transition: &CeremonyTransition,
102 step_id: &StepId,
103 records: &BTreeMap<StepId, StepExecutionRecord>,
104 ) -> bool {
105 self.repeat_requirement_is_satisfied(step_id, records)
106 || self.transition_waives_exhausted_repeat(transition, step_id, records)
107 }
108
109 fn transition_waives_exhausted_repeat(
110 &self,
111 transition: &CeremonyTransition,
112 step_id: &StepId,
113 records: &BTreeMap<StepId, StepExecutionRecord>,
114 ) -> bool {
115 transition.required_guards().iter().any(|guard_name| {
116 self.guards.get(guard_name).is_some_and(|guard| {
117 matches!(
118 guard.condition(),
119 GuardCondition::StepRepeatExhausted(condition)
120 if condition.step_id() == step_id
121 && self.step_repeat_is_exhausted_on_transition(
122 transition,
123 step_id,
124 records,
125 )
126 )
127 })
128 })
129 }
130
131 fn step_repeat_is_exhausted_on_transition(
132 &self,
133 transition: &CeremonyTransition,
134 step_id: &StepId,
135 records: &BTreeMap<StepId, StepExecutionRecord>,
136 ) -> bool {
137 let Some(step) = self.step(step_id) else {
138 return false;
139 };
140 if step.state_id() != transition.from() {
141 return false;
142 }
143 let Some(policy) = step.repeat_policy() else {
144 return false;
145 };
146 records.get(step_id).is_some_and(|record| {
147 record.status().is_success()
148 && !policy.is_satisfied(record.output())
149 && !policy.permits_another_iteration(record.iteration())
150 })
151 }
152
153 fn repeat_requirement_is_satisfied(
154 &self,
155 step_id: &StepId,
156 records: &BTreeMap<StepId, StepExecutionRecord>,
157 ) -> bool {
158 let Some(policy) = self.step(step_id).and_then(CeremonyStep::repeat_policy) else {
159 return true;
160 };
161 records.get(step_id).is_some_and(|record| {
162 record.status().is_success() && policy.is_satisfied(record.output())
163 })
164 }
165}