use core::fmt;
use std::ffi::CStr;
use wasm3x_sys as ffi;
pub type Result<T, E = Error> = core::result::Result<T, E>;
#[derive(Debug)]
pub struct Error(Box<ErrorRepr>);
#[derive(Debug)]
enum ErrorRepr {
Wasm3(String),
Trap(String),
Mismatch(String),
Host(Box<dyn std::error::Error + Send + Sync + 'static>),
Message(String),
}
impl Error {
pub fn new(message: impl Into<String>) -> Self {
Self(Box::new(ErrorRepr::Message(message.into())))
}
pub fn host<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
{
Self(Box::new(ErrorRepr::Host(error.into())))
}
pub fn is_host(&self) -> bool {
matches!(*self.0, ErrorRepr::Host(_))
}
pub fn is_trap(&self) -> bool {
matches!(*self.0, ErrorRepr::Trap(_))
}
pub(crate) fn mismatch(message: impl Into<String>) -> Self {
Self(Box::new(ErrorRepr::Mismatch(message.into())))
}
pub(crate) fn from_ffi(result: ffi::M3Result) -> Result<()> {
if result.is_null() {
return Ok(());
}
Err(Self(Box::new(ErrorRepr::Wasm3(ffi_message(result)))))
}
pub(crate) fn from_trap(runtime: ffi::IM3Runtime, result: ffi::M3Result) -> Self {
debug_assert!(!result.is_null());
let mut message = ffi_message(result);
unsafe {
let mut info: ffi::M3ErrorInfo = core::mem::zeroed();
ffi::m3_GetErrorInfo(runtime, &mut info);
if !info.message.is_null() {
let detail = CStr::from_ptr(info.message).to_string_lossy();
if !detail.is_empty() && !message.contains(detail.as_ref()) {
message = format!("{message}: {detail}");
}
}
}
Self(Box::new(ErrorRepr::Trap(message)))
}
}
fn ffi_message(result: ffi::M3Result) -> String {
unsafe { CStr::from_ptr(result) }
.to_string_lossy()
.into_owned()
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &*self.0 {
ErrorRepr::Wasm3(msg) => write!(f, "wasm3 error: {msg}"),
ErrorRepr::Trap(msg) => write!(f, "wasm trap: {msg}"),
ErrorRepr::Mismatch(msg) => write!(f, "type mismatch: {msg}"),
ErrorRepr::Host(err) => write!(f, "host error: {err}"),
ErrorRepr::Message(msg) => f.write_str(msg),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &*self.0 {
ErrorRepr::Host(err) => Some(&**err),
_ => None,
}
}
}