made-core 0.8.0

Domain core of MADE: entities, value objects, events, ports. No IO.
Documentation
use crate::entities::ceremony_commands::RenewStepLease;
use crate::entities::ceremony_events::StepLeaseRenewed;
use crate::entities::{CeremonyDefinition, CeremonyEvent, CeremonyInstance};
use crate::error::DomainError;

impl CeremonyInstance {
    #[must_use]
    pub fn lease_renewal(
        &self,
        id: &crate::value_objects::IdempotencyKey,
    ) -> Option<&StepLeaseRenewed> {
        self.lease_renewals.get(id)
    }

    pub(super) fn decide_renew_step_lease(
        &self,
        command: &RenewStepLease,
        definition: &CeremonyDefinition,
    ) -> Result<Vec<CeremonyEvent>, DomainError> {
        self.require_definition(definition)?;
        if let Some(request) = &command.request {
            if let Some(accepted) = self.lease_renewal(&request.id) {
                if accepted.request.as_ref() != Some(request)
                    || accepted.step_id != command.step_id
                    || accepted.claim_fence != command.claim_fence
                    || accepted.lease_owner_id != command.lease_owner_id
                {
                    return Err(DomainError::Conflict {
                        what: "step_lease_renewal_request",
                    });
                }
                // Replay is a receipt of the earlier acceptance, not new authority.
                return Ok(Vec::new());
            }
        }
        if self.is_ended() || self.is_terminal(definition) {
            return Err(DomainError::InvariantViolated {
                reason: "ended ceremony cannot renew a lease",
            });
        }
        self.require_step_claim_fence(&command.step_id, &command.claim_fence)?;
        let record = self
            .step_record(&command.step_id)
            .ok_or(DomainError::NotFound {
                what: "ceremony_step",
            })?;
        let lease = record
            .lease()
            .ok_or(DomainError::NotFound { what: "step_lease" })?;
        if lease.owner_id() != &command.lease_owner_id || !record.has_live_lease_at(command.now) {
            return Err(DomainError::InvariantViolated {
                reason: "only the live lease owner may renew",
            });
        }
        if record.effective_lease_expires_at() != Some(command.expected_expires_at) {
            return Err(DomainError::Conflict {
                what: "step_lease_expiry",
            });
        }
        if command.expires_at < command.expected_expires_at || command.expires_at <= command.now {
            return Err(DomainError::InvariantViolated {
                reason: "renewal cannot shorten the effective lease expiry",
            });
        }
        let deadline = self
            .step_deadlines
            .get(&command.step_id)
            .map(crate::value_objects::StepDeadline::at)
            .into_iter()
            .chain(
                self.state_deadline
                    .as_ref()
                    .map(crate::value_objects::StateDeadline::at),
            )
            .chain(
                self.ceremony_deadline
                    .map(crate::value_objects::CeremonyDeadline::at),
            )
            .min();
        if deadline.is_some_and(|at| command.now >= at || command.expires_at > at) {
            return Err(DomainError::InvariantViolated {
                reason: "renewal cannot exceed the absolute deadline",
            });
        }
        if command.expires_at == command.expected_expires_at && command.request.is_none() {
            return Ok(Vec::new());
        }
        Ok(vec![CeremonyEvent::StepLeaseRenewed(StepLeaseRenewed {
            request: command.request.clone(),
            step_id: command.step_id.clone(),
            claim_fence: command.claim_fence.clone(),
            lease_owner_id: command.lease_owner_id.clone(),
            previous_expires_at: command.expected_expires_at,
            expires_at: command.expires_at,
            renewed_at: command.now,
        })])
    }
}