1use std::sync::Arc;
6use wasm_bindgen::prelude::*;
7use workflow_core::sendable::Sendable;
8
9pub trait JsErrorExtension {
10 fn message(&self) -> String;
11}
12
13impl JsErrorExtension for JsError {
14 fn message(&self) -> String {
15 let inner = JsValue::from(self.clone());
16 let msg = js_sys::Reflect::get(&inner, &JsValue::from_str("message"))
17 .expect("unable to get error message");
18 msg.as_string()
19 .expect("unable to convert error message to string")
20 }
21}
22
23pub trait JsValueErrorTrait {
24 fn message(&self) -> String;
25}
26
27impl JsValueErrorTrait for JsValue {
28 fn message(&self) -> String {
29 let msg = js_sys::Reflect::get(self, &JsValue::from_str("message"))
30 .expect("unable to get error message");
31 msg.as_string()
32 .expect("unable to convert error message to string")
33 }
34}
35
36struct Inner {
37 name: Option<String>,
38 message: Option<String>,
39 cause: Option<String>,
40 stack: Option<String>,
41 code: Option<String>,
42 origin: Sendable<JsValue>,
44}
45
46#[derive(Clone)]
47pub struct JsErrorData {
48 inner: Arc<Inner>,
49}
50
51impl std::error::Error for JsErrorData {}
52
53impl JsErrorData {
54 pub fn name(&self) -> &Option<String> {
55 &self.inner.name
56 }
57
58 pub fn message(&self) -> &Option<String> {
59 &self.inner.message
60 }
61
62 pub fn cause(&self) -> &Option<String> {
63 &self.inner.cause
64 }
65
66 pub fn stack(&self) -> &Option<String> {
67 &self.inner.stack
68 }
69
70 pub fn code(&self) -> &Option<String> {
71 &self.inner.code
72 }
73}
74
75impl std::fmt::Debug for JsErrorData {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.debug_struct("JsErrorData")
78 .field("name", &self.inner.name)
79 .field("message", &self.inner.message)
80 .field("cause", &self.inner.cause)
81 .field("stack", &self.inner.stack)
82 .field("code", &self.inner.code)
83 .finish()
84 }
85}
86
87impl std::fmt::Display for JsErrorData {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(
90 f,
91 "{}",
92 self.inner
93 .message
94 .clone()
95 .unwrap_or_else(|| "N/A".to_string())
96 )
97 }
98}
99
100impl From<JsValue> for JsErrorData {
101 fn from(error: JsValue) -> Self {
102 let name = js_sys::Reflect::get(&error, &"name".into())
103 .ok()
104 .and_then(|v| v.as_string());
105 let message = js_sys::Reflect::get(&error, &"message".into())
106 .ok()
107 .and_then(|v| v.as_string());
108 let cause = js_sys::Reflect::get(&error, &"cause".into())
109 .ok()
110 .and_then(|v| v.as_string());
111 let stack = js_sys::Reflect::get(&error, &"stack".into())
112 .ok()
113 .and_then(|v| v.as_string());
114 let code = js_sys::Reflect::get(&error, &"code".into())
115 .ok()
116 .and_then(|v| v.as_string());
117
118 Self {
119 inner: Arc::new(Inner {
120 name,
121 message,
122 cause,
123 stack,
124 code,
125 origin: Sendable::new(error),
126 }),
127 }
128 }
129}
130
131impl From<JsError> for JsErrorData {
132 fn from(error: JsError) -> Self {
133 JsValue::from(error).into()
134 }
135}
136
137impl From<JsErrorData> for JsValue {
138 fn from(error: JsErrorData) -> Self {
139 error.inner.origin.as_ref().clone()
140 }
141}