rust_jsc/
error.rs

1use std::ops::Deref;
2
3use rust_jsc_sys::{JSObjectMakeError, JSObjectMakeTypeError, JSValueRef};
4
5use crate::{JSContext, JSError, JSObject, JSResult, JSString, JSValue};
6
7impl JSError {
8    /// Creates a new `JSError` object.
9    /// This is the same as `new Error()`.
10    ///
11    /// # Arguments
12    ///
13    /// * `ctx` - The JavaScript context.
14    /// * `args` - The arguments to pass to the error constructor.
15    ///
16    /// # Example
17    ///
18    /// ```
19    /// use rust_jsc::{JSContext, JSError};
20    ///
21    /// let ctx = JSContext::new();
22    /// let error = JSError::new(&ctx, &[]).unwrap();
23    /// assert_eq!(error.name().unwrap().to_string(), "Error");
24    /// ```
25    ///
26    /// # Returns
27    ///
28    /// A new `JSError` object.
29    pub fn new(ctx: &JSContext, args: &[JSValue]) -> JSResult<Self> {
30        let mut exception: JSValueRef = std::ptr::null_mut();
31        let args: Vec<JSValueRef> = args.iter().map(|arg| arg.inner).collect();
32
33        let result = unsafe {
34            JSObjectMakeError(ctx.inner, args.len(), args.as_ptr(), &mut exception)
35        };
36
37        if !exception.is_null() {
38            let value = JSValue::new(exception, ctx.inner);
39            return Err(JSError::from(value));
40        }
41
42        Ok(Self::from(JSObject::from_ref(result, ctx.inner)))
43    }
44
45    /// Creates a new `JSError` object with the given message.
46    /// This is the same as `new TypeError(message)`
47    ///
48    /// # Arguments
49    ///
50    /// * `ctx` - The JavaScript context.
51    /// * `message` - The error message.
52    ///
53    /// # Example
54    ///
55    /// ```
56    /// use rust_jsc::{JSContext, JSError};
57    ///
58    /// let ctx = JSContext::new();
59    /// let error = JSError::new_typ(&ctx, "test error").unwrap();
60    /// assert_eq!(error.name().unwrap().to_string(), "TypeError");
61    /// assert_eq!(error.message().unwrap().to_string(), "test error");
62    /// ```
63    ///
64    /// # Returns
65    ///
66    /// A new `JSError` of type `TypeError`.
67    pub fn new_typ(ctx: &JSContext, message: impl Into<JSString>) -> JSResult<Self> {
68        let mut exception: JSValueRef = std::ptr::null_mut();
69
70        let result = unsafe {
71            JSObjectMakeTypeError(ctx.inner, message.into().inner, &mut exception)
72        };
73
74        if !exception.is_null() {
75            let value = JSValue::new(exception, ctx.inner);
76            return Err(JSError::from(value));
77        }
78
79        Ok(Self::from(JSObject::from_ref(result, ctx.inner)))
80    }
81
82    pub fn new_typ_raw(ctx: &JSContext, message: impl Into<JSString>) -> JSValueRef {
83        let mut exception: JSValueRef = std::ptr::null_mut();
84
85        let result = unsafe {
86            JSObjectMakeTypeError(ctx.inner, message.into().inner, &mut exception)
87        };
88
89        if !exception.is_null() {
90            return exception;
91        }
92
93        result
94    }
95
96    pub fn with_message(ctx: &JSContext, message: impl Into<JSString>) -> JSResult<Self> {
97        let args = [JSValue::string(ctx, message)];
98        Self::new(ctx, &args)
99    }
100
101    pub fn name(&self) -> JSResult<JSString> {
102        self.object.get_property("name")?.as_string()
103    }
104
105    pub fn message(&self) -> JSResult<JSString> {
106        self.object.get_property("message")?.as_string()
107    }
108
109    pub fn cause(&self) -> JSResult<JSValue> {
110        self.object.get_property("cause")
111    }
112
113    pub fn stack(&self) -> JSResult<JSString> {
114        self.object.get_property("stack")?.as_string()
115    }
116
117    pub fn set_cause(&self, cause: &JSValue) -> JSResult<()> {
118        self.object.set_property("cause", cause, Default::default())
119    }
120
121    pub fn set_stack(&self, stack: &JSValue) -> JSResult<()> {
122        self.object.set_property("stack", stack, Default::default())
123    }
124}
125
126impl std::fmt::Display for JSError {
127    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
128        write!(f, "JavaScript error: {:?}", self.message().unwrap())
129    }
130}
131
132impl std::error::Error for JSError {}
133
134impl From<JSValue> for JSError {
135    fn from(value: JSValue) -> Self {
136        Self {
137            object: value.as_object().unwrap(),
138        }
139    }
140}
141
142impl Deref for JSError {
143    type Target = JSValue;
144
145    fn deref(&self) -> &JSValue {
146        &self.object.value
147    }
148}
149
150impl From<JSError> for JSValue {
151    fn from(error: JSError) -> Self {
152        error.object.into()
153    }
154}
155
156impl From<JSError> for JSObject {
157    fn from(error: JSError) -> Self {
158        error.object
159    }
160}
161
162impl From<JSObject> for JSError {
163    fn from(object: JSObject) -> Self {
164        Self { object }
165    }
166}
167
168impl From<JSError> for JSValueRef {
169    fn from(error: JSError) -> Self {
170        error.object.value.inner
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn test_type_error() {
180        let ctx = JSContext::new();
181        let error = JSError::new_typ(&ctx, "test error").unwrap();
182        assert_eq!(error.name().unwrap().to_string(), "TypeError");
183        assert_eq!(error.message().unwrap().to_string(), "test error");
184
185        let global_object = ctx.global_object();
186        global_object
187            .set_property("myError", &error, Default::default())
188            .unwrap();
189
190        let result = ctx.evaluate_script("myError instanceof TypeError", None);
191        assert!(result.is_ok());
192        assert_eq!(result.unwrap().as_boolean(), true);
193    }
194
195    #[test]
196    fn test_error() {
197        let ctx = JSContext::new();
198        let error = JSError::with_message(&ctx, "test error").unwrap();
199        assert_eq!(error.name().unwrap().to_string(), "Error");
200        assert_eq!(error.message().unwrap().to_string(), "test error");
201
202        let global_object = ctx.global_object();
203        global_object
204            .set_property("myError", &error, Default::default())
205            .unwrap();
206
207        let result = ctx.evaluate_script("myError instanceof Error", None);
208        assert!(result.is_ok());
209        assert_eq!(result.unwrap().as_boolean(), true);
210    }
211}