use alloc::boxed::Box;
use alloc::string::{String, ToString as _};
use core::{error, fmt};
use rquickjs::{Ctx, Error as JSError, Exception, Value};
use serde::{de, ser};
pub struct Error(Box<ErrorImpl>);
impl Error {
pub(crate) fn new(msg: impl Into<ErrorImpl>) -> Self {
Error(Box::new(msg.into()))
}
pub fn catch<'js>(self, ctx: &Ctx<'js>) -> CaughtError<'js> {
self.0.catch(ctx)
}
}
pub type Result<T> = core::result::Result<T, Error>;
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error({})", self.0)
}
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&*self.0, f)
}
}
impl de::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Error(Box::new(ErrorImpl::Message(msg.to_string())))
}
}
impl ser::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Error(Box::new(ErrorImpl::Message(msg.to_string())))
}
}
#[derive(Debug)]
pub enum ErrorImpl {
Message(String),
Rquickjs(JSError),
}
impl ErrorImpl {
pub fn catch<'js>(self, ctx: &Ctx<'js>) -> CaughtError<'js> {
match self {
ErrorImpl::Message(msg) => CaughtError::Message(msg),
ErrorImpl::Rquickjs(JSError::Exception) => {
let value = ctx.catch();
if let Some(ex) = value
.as_object()
.and_then(|x| Exception::from_object(x.clone()))
{
CaughtError::Exception(ex)
} else {
CaughtError::Value(value)
}
}
ErrorImpl::Rquickjs(e) => CaughtError::Error(e),
}
}
}
impl fmt::Display for ErrorImpl {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ErrorImpl::Message(msg) => write!(f, "{msg}"),
ErrorImpl::Rquickjs(e) => write!(f, "JSError: {e}"),
}
}
}
impl From<&str> for ErrorImpl {
fn from(value: &str) -> Self {
ErrorImpl::Message(value.to_string())
}
}
impl From<JSError> for ErrorImpl {
fn from(value: JSError) -> Self {
ErrorImpl::Rquickjs(value)
}
}
#[derive(Debug)]
pub enum CaughtError<'js> {
Exception(Exception<'js>),
Value(Value<'js>),
Error(JSError),
Message(String),
}
impl<'js> fmt::Display for CaughtError<'js> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CaughtError::Error(e) => e.fmt(f),
CaughtError::Exception(e) => e.fmt(f),
CaughtError::Value(e) => {
writeln!(f, "Exception generated by quickjs: {e:?}")
}
CaughtError::Message(msg) => write!(f, "{msg}"),
}
}
}
impl<'js> error::Error for CaughtError<'js> {}