Skip to main content

rquickjs_serde/
err.rs

1use alloc::boxed::Box;
2use alloc::string::{String, ToString as _};
3use core::{error, fmt};
4
5use rquickjs::{Ctx, Error as JSError, Exception, Value};
6use serde::{de, ser};
7
8/// This type represents all possible errors that can occur when serializing or
9/// deserializing JS values.
10pub struct Error(Box<ErrorImpl>);
11
12impl Error {
13    pub(crate) fn new(msg: impl Into<ErrorImpl>) -> Self {
14        Error(Box::new(msg.into()))
15    }
16
17    pub fn catch<'js>(self, ctx: &Ctx<'js>) -> CaughtError<'js> {
18        self.0.catch(ctx)
19    }
20}
21
22/// Alias for a `Result` with the error type `rquickjs_serde::Error`.
23pub type Result<T> = core::result::Result<T, Error>;
24
25impl fmt::Debug for Error {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        write!(f, "Error({})", self.0)
28    }
29}
30
31impl error::Error for Error {}
32
33impl fmt::Display for Error {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        fmt::Display::fmt(&*self.0, f)
36    }
37}
38
39impl de::Error for Error {
40    fn custom<T: fmt::Display>(msg: T) -> Self {
41        Error(Box::new(ErrorImpl::Message(msg.to_string())))
42    }
43}
44
45impl ser::Error for Error {
46    fn custom<T: fmt::Display>(msg: T) -> Self {
47        Error(Box::new(ErrorImpl::Message(msg.to_string())))
48    }
49}
50
51/// The internal representation of an error.
52///
53/// This enum represents various errors that can occur during JS value serialization or deserialization,
54/// including UTF-8 conversion errors, and errors originating from the `rquickjs` library.
55#[derive(Debug)]
56pub enum ErrorImpl {
57    /// A generic error message
58    Message(String),
59    /// An error originating from the `rquickjs` library.
60    Rquickjs(JSError),
61}
62
63impl ErrorImpl {
64    pub fn catch<'js>(self, ctx: &Ctx<'js>) -> CaughtError<'js> {
65        match self {
66            ErrorImpl::Message(msg) => CaughtError::Message(msg),
67            ErrorImpl::Rquickjs(JSError::Exception) => {
68                let value = ctx.catch();
69                if let Some(ex) = value
70                    .as_object()
71                    .and_then(|x| Exception::from_object(x.clone()))
72                {
73                    CaughtError::Exception(ex)
74                } else {
75                    CaughtError::Value(value)
76                }
77            }
78            ErrorImpl::Rquickjs(e) => CaughtError::Error(e),
79        }
80    }
81}
82
83impl fmt::Display for ErrorImpl {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        match self {
86            ErrorImpl::Message(msg) => write!(f, "{msg}"),
87            // JSError prefix is used by Javy because the serde_transcode.
88            // Keep it as-is for compatibility.
89            ErrorImpl::Rquickjs(e) => write!(f, "JSError: {e}"),
90        }
91    }
92}
93
94impl From<&str> for ErrorImpl {
95    fn from(value: &str) -> Self {
96        ErrorImpl::Message(value.to_string())
97    }
98}
99
100impl From<JSError> for ErrorImpl {
101    fn from(value: JSError) -> Self {
102        ErrorImpl::Rquickjs(value)
103    }
104}
105
106/// An error type containing possible thrown exception values.
107#[derive(Debug)]
108pub enum CaughtError<'js> {
109    /// Error was an exception and an instance of Error
110    Exception(Exception<'js>),
111    /// Error was an exception but not an instance of Error.
112    Value(Value<'js>),
113    /// Error wasn't an exception
114    Error(JSError),
115    /// A generic error message
116    Message(String),
117}
118
119impl<'js> fmt::Display for CaughtError<'js> {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        match self {
122            CaughtError::Error(e) => e.fmt(f),
123            CaughtError::Exception(e) => e.fmt(f),
124            CaughtError::Value(e) => {
125                writeln!(f, "Exception generated by quickjs: {e:?}")
126            }
127            CaughtError::Message(msg) => write!(f, "{msg}"),
128        }
129    }
130}
131
132impl<'js> error::Error for CaughtError<'js> {}