Skip to main content

strontium_core/
run.rs

1use crate::trace::{ReproBundle, TraceBuffer, TraceConfig};
2use std::fmt;
3use std::time::Duration;
4
5#[derive(Debug, Clone)]
6pub struct Config {
7    pub max_virtual_time: Duration,
8    pub max_steps: u64,
9    pub trace: TraceConfig,
10}
11
12impl Default for Config {
13    fn default() -> Self {
14        Self {
15            max_virtual_time: Duration::from_secs(60),
16            max_steps: 1_000_000,
17            trace: TraceConfig::default(),
18        }
19    }
20}
21
22#[derive(Debug)]
23pub enum Outcome {
24    Ok,
25    Panicked(Box<dyn std::any::Any + Send>),
26}
27
28pub struct RunResult {
29    pub seed: u64,
30    pub virtual_time_elapsed: Duration,
31    pub steps_executed: u64,
32    pub outcome: Outcome,
33    pub trace: TraceBuffer,
34    pub decision_log: Vec<usize>,
35    pub repro_bundle: Option<ReproBundle>,
36}
37
38impl RunResult {
39    pub fn is_ok(&self) -> bool {
40        matches!(self.outcome, Outcome::Ok)
41    }
42
43    pub fn assert_ok(self) {
44        if let Outcome::Panicked(e) = self.outcome {
45            std::panic::resume_unwind(e);
46        }
47    }
48}
49
50impl fmt::Display for RunResult {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match &self.outcome {
53            Outcome::Ok => write!(
54                f,
55                "SIMULATION OK (seed={:#x}, time={:?}, steps={})",
56                self.seed, self.virtual_time_elapsed, self.steps_executed
57            ),
58            Outcome::Panicked(_) => write!(
59                f,
60                "SIMULATION FAILED (seed={:#x}, time={:?}, steps={})\nReproduce: set seed={:#x}",
61                self.seed, self.virtual_time_elapsed, self.steps_executed, self.seed
62            ),
63        }
64    }
65}
66
67#[derive(Debug, Clone)]
68pub enum InvariantViolation {
69    DuplicatePath {
70        engine_idx: usize,
71        path: String,
72    },
73    OrphanedChild {
74        engine_idx: usize,
75        child: String,
76        parent: String,
77    },
78    CrossEngineDuplicate {
79        path: String,
80        engines: Vec<usize>,
81    },
82    InconsistentGeneration {
83        engine_idx: usize,
84        path: String,
85        r#gen: u32,
86        restarts: u32,
87    },
88}
89
90impl fmt::Display for InvariantViolation {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            Self::DuplicatePath { engine_idx, path } => write!(
94                f,
95                "engine {engine_idx}: duplicate path '{path}' in registry"
96            ),
97            Self::OrphanedChild {
98                engine_idx,
99                child,
100                parent,
101            } => write!(
102                f,
103                "engine {engine_idx}: Running child '{child}' has non-Running parent '{parent}' (INV-003)"
104            ),
105            Self::CrossEngineDuplicate { path, engines } => write!(
106                f,
107                "path '{path}' is Running on multiple engines: {engines:?}"
108            ),
109            Self::InconsistentGeneration {
110                engine_idx,
111                path,
112                r#gen,
113                restarts,
114            } => write!(
115                f,
116                "engine {engine_idx}: actor '{path}' has inconsistent gen/restarts: gen={}, restarts={restarts}",
117                r#gen
118            ),
119        }
120    }
121}