rquickjs_serde/
err.rs

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