Skip to main content

made_core/entities/ceremony_instance/
guard_decisions.rs

1use super::{
2    AuditActorKind, CeremonyDefinition, CeremonyGuardApproval, CeremonyGuardDeferral,
3    CeremonyGuardDeferralContent, CeremonyInstance, DomainError, GuardCondition, GuardName,
4    OffsetDateTime, RoleId,
5};
6
7impl CeremonyInstance {
8    /// Approving is checked the way deferring is. It used to take no
9    /// definition at all, so any name at all could be "approved" —
10    /// which wrote that name into the session context, told the caller
11    /// it had succeeded, and left a session that would never move.
12    ///
13    /// Approving ahead of time is still allowed: unlike a deferral,
14    /// which answers a decision being asked for now, a person may
15    /// settle a guard before the work leading up to it is finished.
16    pub fn approve_guard(
17        &mut self,
18        definition: &CeremonyDefinition,
19        guard_name: &GuardName,
20        approved_by: RoleId,
21        approved_by_kind: AuditActorKind,
22        now: OffsetDateTime,
23    ) -> Result<(), DomainError> {
24        self.require_active(
25            definition,
26            "terminal ceremony instances cannot approve guards",
27        )?;
28        let guard = definition
29            .guards()
30            .get(guard_name)
31            .ok_or(DomainError::NotFound {
32                what: "ceremony_guard",
33            })?;
34        if !matches!(guard.condition(), GuardCondition::HumanApproval) {
35            return Err(DomainError::InvariantViolated {
36                reason: "only human approval guards can be approved",
37            });
38        }
39        self.require_declared_role(definition, &approved_by)?;
40        self.context = self.context.clone().with_guard_approval(guard_name)?;
41        self.guard_approvals.push(CeremonyGuardApproval::record(
42            guard_name.clone(),
43            approved_by,
44            approved_by_kind,
45            now,
46        ));
47        self.updated_at = now;
48        Ok(())
49    }
50
51    pub fn defer_guard(
52        &mut self,
53        definition: &CeremonyDefinition,
54        guard_name: GuardName,
55        content: CeremonyGuardDeferralContent,
56        deferred_by: RoleId,
57        deferred_by_kind: AuditActorKind,
58        now: OffsetDateTime,
59    ) -> Result<(), DomainError> {
60        self.require_active(
61            definition,
62            "terminal ceremony instances cannot defer guard decisions",
63        )?;
64        let guard = definition
65            .guards()
66            .get(&guard_name)
67            .ok_or(DomainError::NotFound {
68                what: "ceremony_guard",
69            })?;
70        if !matches!(guard.condition(), GuardCondition::HumanApproval) {
71            return Err(DomainError::InvariantViolated {
72                reason: "only human approval guards can be deferred",
73            });
74        }
75        if self.context.is_guard_approved(&guard_name) {
76            return Err(DomainError::InvariantViolated {
77                reason: "approved human guards cannot be deferred",
78            });
79        }
80        let is_currently_required = definition
81            .available_transitions(&self.current_state)
82            .any(|transition| transition.required_guards().contains(&guard_name));
83        if !is_currently_required {
84            return Err(DomainError::InvariantViolated {
85                reason: "human guard is not required from the current state",
86            });
87        }
88
89        self.require_declared_role(definition, &deferred_by)?;
90        self.guard_deferrals.push(CeremonyGuardDeferral::record(
91            guard_name,
92            deferred_by,
93            deferred_by_kind,
94            content,
95            now,
96        ));
97        self.updated_at = now;
98        Ok(())
99    }
100}