Skip to main content

confium_sandbox_wasm/
error.rs

1//! Error type for the WASM sandbox crate.
2//!
3//! Mirrors the snafu-based pattern used across confium crates. All
4//! public [`crate::Sandbox`] / [`crate::SandboxInstance`] operations
5//! surface these via [`Result`](crate::Result).
6
7use snafu::Backtrace;
8use snafu::Snafu;
9
10/// Boxed error source — any host-side error fits here.
11pub type SourceError = Box<dyn std::error::Error + Send + Sync>;
12
13/// Wrap a wasmtime error (which does NOT impl `std::error::Error` in
14/// wasmtime 27 — it's `anyhow::Error`-style) into something that does,
15/// so it can flow through snafu's source field.
16#[derive(Debug)]
17pub struct WasmtimeError(pub String);
18
19impl std::fmt::Display for WasmtimeError {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        f.write_str(&self.0)
22    }
23}
24
25impl std::error::Error for WasmtimeError {}
26
27impl WasmtimeError {
28    /// Construct from anything wasmtime returns (which all impl
29    /// `Display`).
30    pub fn from_display<E: std::fmt::Display>(e: E) -> SourceError {
31        Box::new(WasmtimeError(e.to_string()))
32    }
33}
34
35#[derive(Snafu, Debug)]
36#[snafu(visibility(pub))]
37pub enum Error {
38    #[snafu(display("WASM module compilation failed: {}", source))]
39    ModuleCompile {
40        source: SourceError,
41        backtrace: Backtrace,
42    },
43    #[snafu(display("WASM module instantiation failed: {}", source))]
44    Instantiation {
45        source: SourceError,
46        backtrace: Backtrace,
47    },
48    #[snafu(display("WASM function '{}' not found", function))]
49    FunctionNotFound {
50        function: String,
51        backtrace: Backtrace,
52    },
53    #[snafu(display("WASM function '{}' invocation failed: {}", function, source))]
54    Invocation {
55        function: String,
56        source: SourceError,
57        backtrace: Backtrace,
58    },
59    #[snafu(display("Export '{}' not found in WASM instance", export))]
60    ExportNotFound {
61        export: String,
62        backtrace: Backtrace,
63    },
64    #[snafu(display("Argument type mismatch for function '{}'", function))]
65    ArgumentType {
66        function: String,
67        backtrace: Backtrace,
68    },
69    #[snafu(display("Capability denied: {}", reason))]
70    CapabilityDenied {
71        reason: String,
72        backtrace: Backtrace,
73    },
74    #[snafu(display("Host import '{}' failed: {}", import, reason))]
75    HostImport {
76        import: String,
77        reason: String,
78        backtrace: Backtrace,
79    },
80    #[snafu(display("Wasmtime engine error: {}", source))]
81    Engine {
82        source: SourceError,
83        backtrace: Backtrace,
84    },
85}
86
87impl Error {
88    /// Numeric error code for the WASM sandbox ABI. Disjoint from
89    /// core (1..) and TC (0x1000..) codes; sandbox codes begin at
90    /// 0x2000.
91    pub fn code(&self) -> u32 {
92        match self {
93            Error::ModuleCompile { .. } => 0x2000,
94            Error::Instantiation { .. } => 0x2001,
95            Error::FunctionNotFound { .. } => 0x2002,
96            Error::Invocation { .. } => 0x2003,
97            Error::ExportNotFound { .. } => 0x2004,
98            Error::ArgumentType { .. } => 0x2005,
99            Error::CapabilityDenied { .. } => 0x2006,
100            Error::HostImport { .. } => 0x2007,
101            Error::Engine { .. } => 0x2008,
102        }
103    }
104}
105
106pub type Result<T> = std::result::Result<T, Error>;
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn error_codes_are_disjoint_from_core_and_tc() {
114        // Sandbox codes begin at 0x2000 (core = 1.., TC = 0x1000..).
115        let err = FunctionNotFoundSnafu {
116            function: "x".to_string(),
117        }
118        .build();
119        assert!(err.code() >= 0x2000);
120        assert!(err.code() < 0x3000);
121    }
122
123    #[test]
124    fn capability_denied_code() {
125        let err = CapabilityDeniedSnafu {
126            reason: "test".to_string(),
127        }
128        .build();
129        assert_eq!(err.code(), 0x2006);
130    }
131}