use rustev_contract::{
evidence::StepStatus,
judgment::{Derivation, Unresolved},
};
use std::{
panic::{AssertUnwindSafe, catch_unwind},
sync::atomic::{AtomicU64, Ordering},
};
#[derive(Debug, Clone, PartialEq)]
pub struct StepProgress {
pub step_id: String,
pub instance_ids: Vec<String>,
pub outcome: Result<(), Unresolved>,
pub derivation: Derivation,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SkippedStep {
pub step_id: String,
pub instance_ids: Vec<String>,
pub derivation: Derivation,
}
pub trait ProgressObserver: Send + Sync {
fn admitted(&self) {}
fn step(&self, progress: StepProgress);
fn skipped(&self, _step: SkippedStep) {}
}
pub(crate) struct Observing<'a> {
pub observer: &'a dyn ProgressObserver,
pub panics: &'a AtomicU64,
}
impl Observing<'_> {
pub fn call(&self, f: impl FnOnce(&dyn ProgressObserver)) {
if catch_unwind(AssertUnwindSafe(|| f(self.observer))).is_err() {
self.panics.fetch_add(1, Ordering::Relaxed);
}
}
pub fn remaining(
&self,
evidence: &rustev_contract::evidence::EvidenceRecord,
requests: &[rustev_contract::run::RequestRecord],
plan: &rustev_contract::plan::Plan,
) {
for step in &evidence.steps {
if requests
.iter()
.any(|r| r.step == step.step && r.instance == step.instance)
{
continue;
}
let compiled = || {
plan.steps
.iter()
.find(|s| s.id == step.step)
.expect("recorded step belongs to plan")
.derivation
};
let (outcome, derivation) = match &step.status {
StepStatus::Skipped { .. } => {
let derivation = compiled();
self.call(|o| {
o.skipped(SkippedStep {
step_id: step.step.clone(),
instance_ids: step.instance.clone(),
derivation,
})
});
continue;
}
StepStatus::Resolved { derivation, .. } => (Ok(()), *derivation),
StepStatus::Unresolved(reason) => (Err(reason.clone()), compiled()),
};
self.call(|o| {
o.step(StepProgress {
step_id: step.step.clone(),
instance_ids: step.instance.clone(),
outcome,
derivation,
})
});
}
}
}