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