Skip to main content

rustev_runtime/
progress.rs

1//! Optional value-free progress observation (spec 042, 3.7.3).
2use rustev_contract::{
3    evidence::StepStatus,
4    judgment::{Derivation, Unresolved},
5};
6use std::{
7    panic::{AssertUnwindSafe, catch_unwind},
8    sync::atomic::{AtomicU64, Ordering},
9};
10
11/// A recorded result, without value bytes or authorization semantics.
12#[derive(Debug, Clone, PartialEq)]
13pub struct StepProgress {
14    pub step_id: String,
15    pub instance_ids: Vec<String>,
16    pub outcome: Result<(), Unresolved>,
17    pub derivation: Derivation,
18}
19
20/// Synchronous, non-blocking callbacks. Implementations must perform no I/O,
21/// acquire no contended locks, and never wait for a consumer. Panics are counted
22/// and contained by the runtime. Observation cannot alter evidence.
23pub trait ProgressObserver: Send + Sync {
24    /// Admission was acquired; this decision is no longer queued.
25    fn admitted(&self) {}
26    fn step(&self, progress: StepProgress);
27}
28
29pub(crate) struct Observing<'a> {
30    pub observer: &'a dyn ProgressObserver,
31    pub panics: &'a AtomicU64,
32}
33impl Observing<'_> {
34    pub fn call(&self, f: impl FnOnce(&dyn ProgressObserver)) {
35        if catch_unwind(AssertUnwindSafe(|| f(self.observer))).is_err() {
36            self.panics.fetch_add(1, Ordering::Relaxed);
37        }
38    }
39    pub fn remaining(
40        &self,
41        evidence: &rustev_contract::evidence::EvidenceRecord,
42        requests: &[rustev_contract::run::RequestRecord],
43        plan: &rustev_contract::plan::Plan,
44    ) {
45        for step in &evidence.steps {
46            // Supplied semantic instances were already observed at supply time.
47            if requests
48                .iter()
49                .any(|r| r.step == step.step && r.instance == step.instance)
50            {
51                continue;
52            }
53            let (outcome, derivation) = match &step.status {
54                StepStatus::Skipped { .. } => continue,
55                StepStatus::Resolved { derivation, .. } => (Ok(()), *derivation),
56                // Unresolved values have no dynamic derivation in core evidence.
57                // Report the compiled, conservative derivation bound.
58                StepStatus::Unresolved(reason) => (
59                    Err(reason.clone()),
60                    plan.steps
61                        .iter()
62                        .find(|s| s.id == step.step)
63                        .expect("recorded step belongs to plan")
64                        .derivation,
65                ),
66            };
67            self.call(|o| {
68                o.step(StepProgress {
69                    step_id: step.step.clone(),
70                    instance_ids: step.instance.clone(),
71                    outcome,
72                    derivation,
73                })
74            });
75        }
76    }
77}