use crate::RuntimeError;
use std::error::Error;
use std::fmt;
use wasm_bindgen::{prelude::*, JsValue};
use wasm_bindgen_downcast::DowncastJS;
#[derive(Debug)]
enum InnerTrap {
User(Box<dyn Error + Send + Sync>),
Js(JsValue),
}
#[wasm_bindgen]
#[derive(Debug, DowncastJS)]
pub struct Trap {
inner: InnerTrap,
}
unsafe impl Send for Trap {}
unsafe impl Sync for Trap {}
impl Trap {
pub fn user(error: Box<dyn Error + Send + Sync>) -> Self {
Self {
inner: InnerTrap::User(error),
}
}
pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
match self.inner {
InnerTrap::User(err) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
_ => Err(self),
}
}
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
match &self.inner {
InnerTrap::User(err) if err.is::<T>() => err.downcast_ref::<T>(),
_ => None,
}
}
pub fn is<T: Error + 'static>(&self) -> bool {
match &self.inner {
InnerTrap::User(err) => err.is::<T>(),
_ => false,
}
}
}
impl std::error::Error for Trap {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.inner {
InnerTrap::User(err) => Some(&**err),
_ => None,
}
}
}
impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
InnerTrap::User(e) => write!(f, "user: {}", e),
InnerTrap::Js(value) => write!(f, "js: {:?}", value.as_string()),
}
}
}
impl From<JsValue> for RuntimeError {
fn from(original: JsValue) -> Self {
let trap = Trap::downcast_js(original).unwrap_or_else(|o| Trap {
inner: InnerTrap::Js(o),
});
trap.into()
}
}