use crate::{
model::{
attempt_journal::AttemptJournalRecord, execution_settlement::ExecutionSettlementRecord,
operation_plan::OperationPlanRecord,
},
policy::execution_progress::{
self, AdmittedProgress, ExecutionProgressError, ExecutionProgressView,
},
};
use thiserror::Error;
pub fn validate(
plan: &OperationPlanRecord,
journals: &[&AttemptJournalRecord],
settlement: &ExecutionSettlementRecord,
) -> Result<ExecutionProgressView, ExecutionSettlementPolicyError> {
Ok(validate_journals(plan, journals.iter().copied(), settlement)?.project()?)
}
pub(crate) fn validate_journals<'a>(
plan: &'a OperationPlanRecord,
journals: impl ExactSizeIterator<Item = &'a AttemptJournalRecord>,
settlement: &ExecutionSettlementRecord,
) -> Result<AdmittedProgress<'a>, ExecutionSettlementPolicyError> {
if &plan.digest() != settlement.plan_intent() {
return Err(ExecutionSettlementPolicyError::IntentMismatch);
}
let admitted = execution_progress::admit_progress(plan, journals)?;
if let Some(operation) = plan.ordered_operations().find(|operation| {
admitted.journals()[&operation.operation_sequence()]
.applied_receipt()
.is_none()
}) {
return Err(ExecutionSettlementPolicyError::UnsettledOperation(
operation.operation_sequence(),
));
}
if settlement.journals().len() != plan.operations().len() {
return Err(ExecutionSettlementPolicyError::JournalSetMismatch);
}
for (row, operation) in settlement.journals().iter().zip(plan.operations()) {
if row.operation_sequence() != operation.operation_sequence() {
return Err(ExecutionSettlementPolicyError::JournalSetMismatch);
}
let journal = admitted
.journals()
.get(&row.operation_sequence())
.ok_or(ExecutionSettlementPolicyError::JournalSetMismatch)?;
if &journal.digest() != row.history() {
return Err(ExecutionSettlementPolicyError::HistoryMismatch(
row.operation_sequence(),
));
}
}
Ok(admitted)
}
#[derive(Debug, Eq, Error, PartialEq)]
pub enum ExecutionSettlementPolicyError {
#[error("execution settlement original plan mismatch")]
IntentMismatch,
#[error(transparent)]
Progress(#[from] ExecutionProgressError),
#[error("execution settlement operation {0} is not applied")]
UnsettledOperation(u64),
#[error("execution settlement journal set mismatch")]
JournalSetMismatch,
#[error("execution settlement journal {0} history mismatch")]
HistoryMismatch(u64),
}
#[cfg(test)]
mod tests;