Skip to main content

surrealism_types/
err.rs

1//! Error handling utilities for prefixing errors with context.
2
3use std::fmt::Display;
4use std::num::TryFromIntError;
5use std::time::Duration;
6
7#[derive(thiserror::Error, Debug)]
8pub enum SurrealismError {
9	#[cfg(feature = "host")]
10	#[error("WASM compilation failed: {0}")]
11	Compilation(wasmtime::Error),
12	#[cfg(feature = "host")]
13	#[error("WASM instantiation failed: {0}")]
14	Instantiation(wasmtime::Error),
15	#[error("Function call error: {0}")]
16	FunctionCallError(String),
17	#[error(
18		"Module execution timed out (epoch interrupt). Effective timeout: {effective:?}, context timeout: {context_timeout:?}, module limit: {module_limit:?}"
19	)]
20	Timeout {
21		effective: Option<Duration>,
22		context_timeout: Option<Duration>,
23		module_limit: Option<Duration>,
24	},
25	#[error("Unsupported ABI version: expected {expected}, got {got}")]
26	UnsupportedAbi {
27		expected: u32,
28		got: u32,
29	},
30	#[error("Integer conversion error: {0}")]
31	IntConversion(#[from] TryFromIntError),
32	#[cfg(feature = "host")]
33	#[error("Wasmtime error: {0}")]
34	Wasmtime(#[from] wasmtime::Error),
35	#[error("Other error: {0}")]
36	Other(#[from] anyhow::Error),
37}
38
39impl SurrealismError {
40	/// Whether this error represents a WASM trap — an unrecoverable fault
41	/// during execution that leaves the instance in an undefined state.
42	///
43	/// After a trap the controller must be dropped (not returned to the pool).
44	/// Non-trap errors (guest `Err` returns, host-side encoding failures, etc.)
45	/// go through normal control flow and leave the instance reusable.
46	pub fn is_trap(&self) -> bool {
47		match self {
48			// Epoch interrupt mid-execution — WASM state is undefined.
49			Self::Timeout {
50				..
51			} => true,
52			// Instantiation failure — no usable instance exists.
53			#[cfg(feature = "host")]
54			Self::Instantiation(_) => true,
55			// Wasmtime error from call_async (actual trap) or typed() (type
56			// mismatch). Conservative: treat all as traps since the instance
57			// may be in an unknown state.
58			#[cfg(feature = "host")]
59			Self::Wasmtime(_) => true,
60			// Compilation failure — no instance was ever created.
61			#[cfg(feature = "host")]
62			Self::Compilation(_) => false,
63			// Guest returned Err through normal control flow.
64			Self::FunctionCallError(_) => false,
65			// Host-side errors (encoding, decoding, config) — no WASM ran.
66			Self::UnsupportedAbi {
67				..
68			} => false,
69			Self::IntConversion(_) => false,
70			Self::Other(_) => false,
71		}
72	}
73}
74
75pub type SurrealismResult<T> = std::result::Result<T, SurrealismError>;
76
77pub trait PrefixErr<T> {
78	fn prefix_err<F, S>(self, f: F) -> SurrealismResult<T>
79	where
80		F: FnOnce() -> S,
81		S: Display;
82}
83
84impl<T, E: Display> PrefixErr<T> for std::result::Result<T, E> {
85	fn prefix_err<F, S>(self, f: F) -> SurrealismResult<T>
86	where
87		F: FnOnce() -> S,
88		S: Display,
89	{
90		self.map_err(|e| SurrealismError::Other(anyhow::anyhow!("{}: {}", f(), e)))
91	}
92}