1use core::fmt;
4use std::ffi::CStr;
5
6use wasm3x_sys as ffi;
7
8pub type Result<T, E = Error> = core::result::Result<T, E>;
10
11#[derive(Debug)]
13pub struct Error(Box<ErrorRepr>);
14
15#[derive(Debug)]
16enum ErrorRepr {
17 Wasm3(String),
19 Trap(String),
21 Mismatch(String),
23 Host(Box<dyn std::error::Error + Send + Sync + 'static>),
25 Message(String),
27}
28
29impl Error {
30 pub fn new(message: impl Into<String>) -> Self {
32 Self(Box::new(ErrorRepr::Message(message.into())))
33 }
34
35 pub fn host<E>(error: E) -> Self
39 where
40 E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
41 {
42 Self(Box::new(ErrorRepr::Host(error.into())))
43 }
44
45 pub fn is_host(&self) -> bool {
47 matches!(*self.0, ErrorRepr::Host(_))
48 }
49
50 pub fn is_trap(&self) -> bool {
52 matches!(*self.0, ErrorRepr::Trap(_))
53 }
54
55 pub(crate) fn mismatch(message: impl Into<String>) -> Self {
56 Self(Box::new(ErrorRepr::Mismatch(message.into())))
57 }
58
59 pub(crate) fn from_ffi(result: ffi::M3Result) -> Result<()> {
64 if result.is_null() {
65 return Ok(());
66 }
67 Err(Self(Box::new(ErrorRepr::Wasm3(ffi_message(result)))))
68 }
69
70 pub(crate) fn from_trap(runtime: ffi::IM3Runtime, result: ffi::M3Result) -> Self {
73 debug_assert!(!result.is_null());
74 let mut message = ffi_message(result);
75 unsafe {
77 let mut info: ffi::M3ErrorInfo = core::mem::zeroed();
78 ffi::m3_GetErrorInfo(runtime, &mut info);
79 if !info.message.is_null() {
80 let detail = CStr::from_ptr(info.message).to_string_lossy();
81 if !detail.is_empty() && !message.contains(detail.as_ref()) {
82 message = format!("{message}: {detail}");
83 }
84 }
85 }
86 Self(Box::new(ErrorRepr::Trap(message)))
87 }
88}
89
90fn ffi_message(result: ffi::M3Result) -> String {
92 unsafe { CStr::from_ptr(result) }
94 .to_string_lossy()
95 .into_owned()
96}
97
98impl fmt::Display for Error {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match &*self.0 {
101 ErrorRepr::Wasm3(msg) => write!(f, "wasm3 error: {msg}"),
102 ErrorRepr::Trap(msg) => write!(f, "wasm trap: {msg}"),
103 ErrorRepr::Mismatch(msg) => write!(f, "type mismatch: {msg}"),
104 ErrorRepr::Host(err) => write!(f, "host error: {err}"),
105 ErrorRepr::Message(msg) => f.write_str(msg),
106 }
107 }
108}
109
110impl std::error::Error for Error {
111 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
112 match &*self.0 {
113 ErrorRepr::Host(err) => Some(&**err),
114 _ => None,
115 }
116 }
117}