1#[cfg(any(feature = "replay", feature = "trace-gen", feature = "trace-validation", feature = "rpc"))]
7use std::path::PathBuf;
8use thiserror::Error;
9
10#[cfg(any(feature = "trace-gen", feature = "trace-validation"))]
14#[derive(Debug, Error)]
15#[non_exhaustive]
16pub enum ApalacheError {
17 #[error("Apalache failed (exit code: {exit_code:?}): {message}")]
19 Execution { exit_code: Option<i32>, message: String },
20
21 #[error("Failed to execute Apalache. Is it installed and on PATH? {0}")]
23 NotFound(String),
24
25 #[error("Apalache timed out after {duration:?}")]
27 Timeout { duration: std::time::Duration },
28}
29
30#[cfg(any(feature = "replay", feature = "trace-gen"))]
34#[derive(Debug, Error)]
35#[error("Failed to read directory {path}: {reason}")]
36pub struct DirectoryReadError {
37 pub path: PathBuf,
38 pub reason: String,
39}
40
41#[cfg(any(feature = "replay", feature = "rpc"))]
43#[derive(Debug, Clone)]
44pub enum StepContext {
45 Replay { trace: usize, state: usize },
46 Rpc { run: usize, step: usize },
47}
48
49#[cfg(any(feature = "replay", feature = "rpc"))]
50impl std::fmt::Display for StepContext {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 StepContext::Replay { trace, state } => write!(f, "Trace {trace}, state {state}"),
54 StepContext::Rpc { run, step } => write!(f, "Run {run}, step {step}"),
55 }
56 }
57}
58
59#[cfg(any(feature = "replay", feature = "rpc"))]
63#[derive(Debug, Error)]
64pub enum StepError {
65 #[error("{context}: failed to execute action '{action}': {reason}")]
67 StepExecution { context: StepContext, action: String, reason: String },
68
69 #[error("{context}: failed to deserialize spec state: {reason}")]
71 SpecDeserialize { context: StepContext, reason: String },
72
73 #[error("{context}: failed to extract driver state: {reason}")]
75 DriverStateExtraction { context: StepContext, reason: String },
76
77 #[error("State mismatch at {context} (action: '{action}'):\n{diff}")]
79 StateMismatch { context: StepContext, action: String, diff: String },
80}
81
82#[derive(Debug, Error)]
84#[non_exhaustive]
85pub enum Error {
86 #[cfg(feature = "replay")]
88 #[error("Replay error: {0}")]
89 Replay(#[from] ReplayError),
90
91 #[cfg(feature = "trace-gen")]
93 #[error("Trace generation error: {0}")]
94 TraceGen(#[from] TraceGenError),
95
96 #[cfg(feature = "trace-validation")]
98 #[error("Trace validation error: {0}")]
99 Validation(#[from] ValidationError),
100
101 #[cfg(feature = "rpc")]
103 #[error("RPC error: {0}")]
104 Rpc(#[from] RpcError),
105
106 #[cfg(any(feature = "replay", feature = "rpc"))]
108 #[error("Step error: {0}")]
109 Step(#[from] StepError),
110
111 #[error("Driver error: {0}")]
113 Driver(#[from] DriverError),
114
115 #[error("Builder error: {0}")]
117 Builder(#[from] BuilderError),
118
119 #[error("IO error: {0}")]
121 Io(#[from] std::io::Error),
122
123 #[error("JSON error: {0}")]
125 Json(#[from] serde_json::Error),
126}
127
128#[cfg(feature = "replay")]
130#[derive(Debug, Error)]
131#[non_exhaustive]
132pub enum ReplayError {
133 #[error("Trace {trace}, state {state}: failed to extract MBT vars: {reason}")]
135 MbtVarExtraction {
136 trace: usize,
137 state: usize,
138 reason: String,
139 },
140
141 #[error("Expected ITF state to be a Record, got: {found}")]
143 InvalidStateType { found: String },
144
145 #[error("Failed to parse ITF trace: {0}")]
147 Parse(String),
148
149 #[error(transparent)]
151 DirectoryRead(#[from] DirectoryReadError),
152}
153
154#[cfg(feature = "trace-gen")]
156#[derive(Debug, Error)]
157#[non_exhaustive]
158pub enum TraceGenError {
159 #[error("TLA+ spec not found: {0}")]
161 SpecNotFound(PathBuf),
162
163 #[error("Failed to create temp directory: {0}")]
165 TempDir(String),
166
167 #[error(transparent)]
169 Apalache(#[from] ApalacheError),
170
171 #[error("No ITF traces found in Apalache output directory: {0}")]
173 NoTracesFound(PathBuf),
174
175 #[error("Failed to parse ITF trace {path}: {reason}")]
177 TraceParse { path: PathBuf, reason: String },
178
179 #[error(transparent)]
181 DirectoryRead(#[from] DirectoryReadError),
182}
183
184#[cfg(feature = "trace-validation")]
186#[derive(Debug, Error)]
187#[non_exhaustive]
188pub enum ValidationError {
189 #[error("TraceSpec not found: {0}")]
191 TraceSpecNotFound(PathBuf),
192
193 #[error("Trace file not found: {0}")]
195 TraceFileNotFound(PathBuf),
196
197 #[error("Trace file is empty: {0}")]
199 EmptyTrace(PathBuf),
200
201 #[error("Invalid JSON on line {line}: {reason}")]
203 InvalidJson { line: usize, reason: String },
204
205 #[error("State must serialize to a JSON object, got: {found}")]
207 NonObjectState { found: String },
208
209 #[error("Inconsistent record schema: line {line} has keys {found:?}, expected {expected:?}")]
211 InconsistentSchema {
212 line: usize,
213 expected: Vec<String>,
214 found: Vec<String>,
215 },
216
217 #[error("Unsupported JSON value type at line {line}, field '{field}': {reason}")]
219 UnsupportedType {
220 line: usize,
221 field: String,
222 reason: String,
223 },
224
225 #[error("Float value not supported at line {line}, field '{field}': {value}")]
227 FloatNotSupported {
228 line: usize,
229 field: String,
230 value: f64,
231 },
232
233 #[error("Failed to convert line {line} to TLA+ record: {reason}")]
235 TlaConversion { line: usize, reason: String },
236
237 #[error(transparent)]
239 Apalache(#[from] ApalacheError),
240
241 #[error("Failed to create work directory: {0}")]
243 WorkDir(String),
244
245 #[error("Failed to copy spec file {path}: {reason}")]
247 FileCopy { path: PathBuf, reason: String },
248
249 #[error("IO error: {0}")]
251 Io(#[from] std::io::Error),
252
253 #[error("Cannot emit after StateEmitter has been finished")]
255 EmitterFinished,
256
257 #[error("Inconsistent array element types at field '{field}': expected {expected}, got {found}")]
259 InconsistentArrayType {
260 field: String,
261 expected: String,
262 found: String,
263 },
264}
265
266#[cfg(feature = "rpc")]
268#[derive(Debug, Error)]
269#[non_exhaustive]
270pub enum RpcError {
271 #[error("Failed to create HTTP client: {0}")]
273 ClientCreation(String),
274
275 #[error("Failed to send request to {url}. Is the Apalache server running? {reason}")]
277 RequestFailed { url: String, reason: String },
278
279 #[error("Failed to parse JSON-RPC response: {0}")]
281 ResponseParse(String),
282
283 #[error("Apalache JSON-RPC error {code}: {message}")]
285 JsonRpc { code: i64, message: String },
286
287 #[error("JSON-RPC response missing 'result' field")]
289 MissingResult,
290
291 #[error("Failed to deserialize JSON-RPC result: {0}")]
293 ResultDeserialize(String),
294
295 #[error("Spec file not found: {0}")]
297 SpecNotFound(PathBuf),
298
299 #[error("Failed to read spec file {path}: {reason}")]
301 SpecRead { path: PathBuf, reason: String },
302
303 #[error("Run {run}: Init transition is disabled")]
305 InitDisabled { run: usize },
306
307 #[error("Run {run}: Constant constraints are unsatisfiable")]
309 ConstantsUnsatisfiable { run: usize },
310
311 #[error("Trace has no states")]
313 EmptyTrace,
314
315 #[error("Trace missing 'states' array")]
317 MissingStates,
318
319 #[error("Failed to convert state to ITF Value: {0}")]
321 StateConversion(String),
322}
323
324#[derive(Debug, Error)]
326#[non_exhaustive]
327pub enum BuilderError {
328 #[error("Required field '{field}' was not set on {builder}")]
330 MissingRequiredField { builder: &'static str, field: &'static str },
331}
332
333#[derive(Debug, Error)]
335#[non_exhaustive]
336pub enum DriverError {
337 #[error("Unknown action: {0}")]
339 UnknownAction(String),
340
341 #[error("Action '{action}' failed: {reason}")]
343 ActionFailed { action: String, reason: String },
344
345 #[error("Failed to extract state: {0}")]
347 StateExtraction(String),
348}
349
350pub type TlaResult<T> = std::result::Result<T, Error>;