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}