use super::{
CeremonyDefinition, CeremonyInstance, CeremonyInterventionProvenance,
CeremonyInterventionTarget, DomainError, RoleAction, RoleId,
};
impl CeremonyInstance {
pub(super) fn matches_definition(&self, definition: &CeremonyDefinition) -> bool {
self.definition_name == *definition.name()
&& self.definition_version == *definition.version()
}
pub(super) fn require_definition(
&self,
definition: &CeremonyDefinition,
) -> Result<(), DomainError> {
if self.matches_definition(definition) {
Ok(())
} else {
Err(DomainError::InvariantViolated {
reason: "ceremony instance definition mismatch",
})
}
}
pub(super) fn require_active(
&self,
definition: &CeremonyDefinition,
terminal_reason: &'static str,
) -> Result<(), DomainError> {
self.require_definition(definition)?;
if self.is_terminal(definition) || self.is_ended() {
Err(DomainError::InvariantViolated {
reason: terminal_reason,
})
} else {
Ok(())
}
}
pub(super) fn require_admits_new_work(
&self,
operation: &'static str,
) -> Result<(), DomainError> {
if self.admits_new_work() {
Ok(())
} else {
Err(DomainError::LifecycleRefused {
operation,
phase: self.lifecycle().phase(),
})
}
}
pub(super) fn require_intervention_target(
definition: &CeremonyDefinition,
target: &CeremonyInterventionTarget,
) -> Result<(), DomainError> {
let Some(role_ids) = target.role_ids() else {
return Ok(());
};
for role_id in role_ids {
if definition.role(role_id).is_none() {
return Err(DomainError::NotFound {
what: "ceremony_intervention.target_role",
});
}
if !definition.role_allows(role_id, &RoleAction::respond_to_intervention()) {
return Err(DomainError::InvariantViolated {
reason: "target role cannot respond to ceremony interventions",
});
}
}
Ok(())
}
pub(super) fn require_intervention_provenance(
&self,
definition: &CeremonyDefinition,
requested_by: &RoleId,
target: &CeremonyInterventionTarget,
provenance: &CeremonyInterventionProvenance,
) -> Result<(), DomainError> {
let source = self
.intervention(provenance.source_intervention_id())
.ok_or(DomainError::NotFound {
what: "ceremony_intervention.provenance_source",
})?;
if source.requested_by() != requested_by {
return Err(DomainError::InvariantViolated {
reason: "only the source requester can select an intervention response",
});
}
if !source
.responses()
.iter()
.any(|response| response.role_id() == provenance.source_response_role_id())
{
return Err(DomainError::NotFound {
what: "ceremony_intervention.provenance_response",
});
}
if definition.role(provenance.selected_role_id()).is_none() {
return Err(DomainError::NotFound {
what: "ceremony_intervention.provenance_selected_role",
});
}
if !definition.role_allows(
provenance.selected_role_id(),
&RoleAction::respond_to_intervention(),
) {
return Err(DomainError::InvariantViolated {
reason: "selected intervention role cannot respond",
});
}
if !target.accepts(provenance.selected_role_id()) {
return Err(DomainError::InvariantViolated {
reason: "intervention target does not include the selected role",
});
}
Ok(())
}
pub(super) fn require_declared_role(
&self,
definition: &CeremonyDefinition,
role_id: &RoleId,
) -> Result<(), DomainError> {
self.require_definition(definition)?;
if definition.role(role_id).is_some() {
Ok(())
} else {
Err(DomainError::NotFound {
what: "ceremony_role",
})
}
}
pub(super) fn require_role(
&self,
definition: &CeremonyDefinition,
role_id: &RoleId,
action: &RoleAction,
) -> Result<(), DomainError> {
self.require_definition(definition)?;
if definition.role_allows(role_id, action) {
Ok(())
} else {
Err(DomainError::InvariantViolated {
reason: "ceremony role is not allowed to perform action",
})
}
}
}