#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrapContext {
Init,
OnRequest,
OnResponse,
Dispatch,
}
impl TrapContext {
pub fn is_fault_tolerant(&self) -> bool {
matches!(self, TrapContext::OnResponse)
}
}
#[derive(Debug)]
pub enum TrapResult {
Fatal {
message: String,
context: TrapContext,
},
FaultTolerant {
message: String,
},
}
impl TrapResult {
pub fn from_error<E: std::fmt::Display>(error: E, context: TrapContext) -> Self {
let message = error.to_string();
if context.is_fault_tolerant() {
TrapResult::FaultTolerant { message }
} else {
TrapResult::Fatal { message, context }
}
}
pub fn is_fatal(&self) -> bool {
matches!(self, TrapResult::Fatal { .. })
}
pub fn message(&self) -> &str {
match self {
TrapResult::Fatal { message, .. } => message,
TrapResult::FaultTolerant { message } => message,
}
}
}
#[allow(dead_code)] pub fn classify_trap(trap: &wasmtime::Trap) -> &'static str {
let msg = trap.to_string();
if msg.contains("out of fuel") {
"execution timeout"
} else if msg.contains("unreachable") {
"unreachable code executed (likely panic)"
} else if msg.contains("memory") {
"memory access error"
} else if msg.contains("stack overflow") {
"stack overflow"
} else if msg.contains("indirect call") {
"invalid indirect call"
} else if msg.contains("integer") {
"integer overflow or division error"
} else {
"unknown trap"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn init_is_fatal() {
let result = TrapResult::from_error("init failed", TrapContext::Init);
assert!(result.is_fatal());
}
#[test]
fn on_request_is_fatal() {
let result = TrapResult::from_error("request failed", TrapContext::OnRequest);
assert!(result.is_fatal());
}
#[test]
fn dispatch_is_fatal() {
let result = TrapResult::from_error("dispatch failed", TrapContext::Dispatch);
assert!(result.is_fatal());
}
#[test]
fn on_response_is_fault_tolerant() {
let result = TrapResult::from_error("response failed", TrapContext::OnResponse);
assert!(!result.is_fatal());
}
#[test]
fn trap_context_fault_tolerance() {
assert!(!TrapContext::Init.is_fault_tolerant());
assert!(!TrapContext::OnRequest.is_fault_tolerant());
assert!(!TrapContext::Dispatch.is_fault_tolerant());
assert!(TrapContext::OnResponse.is_fault_tolerant());
}
}