1use std::panic::Location;
12
13use wasm_bindgen::JsValue;
14
15use crate::prelude::*;
16
17pub type Result<T> = core::result::Result<T, Error>;
18
19pub struct Error {
20 context: String,
21 inner: InnerError,
22
23 #[cfg(debug_assertions)]
24 caller: &'static Location<'static>,
25}
26
27impl Encode for Error {
28 #[async_impl]
29 async fn encode_async(&self, writer: &mut impl __utils_macros::AsyncEncoder) -> Result<()> {
30 self.to_string().encode_async(writer).await?;
31 Ok(())
32 }
33 fn encode_size(&self) -> usize {
34 self.context.encode_size()
35 }
36}
37
38impl Decode for Error {
39 #[async_impl]
40 async fn decode_async(reader: &mut impl __utils_macros::AsyncDecoder) -> Result<Self> {
41 let context = String::decode_async(reader).await?;
42 Ok(Self {
43 context,
44 inner: InnerError::None,
45 #[cfg(debug_assertions)]
46 caller: Location::caller(), })
48 }
49}
50
51impl Error {
52 #[track_caller]
53 pub fn new(context: impl std::fmt::Display) -> Self {
54 Self::new_inner(context, InnerError::None)
55 }
56
57 #[track_caller]
58 fn new_inner(context: impl std::fmt::Display, inner: InnerError) -> Self {
59 Error {
60 context: context.to_string(),
61 inner,
62
63 #[cfg(debug_assertions)]
64 caller: Location::caller(),
65 }
66 }
67
68 pub fn context(&self) -> &str {
69 &self.context
70 }
71}
72
73#[cfg(target_arch = "wasm32")]
74fn strigify_js(js_value: &JsValue) -> String {
75 if let Ok(s) = js_sys::JSON::stringify(js_value) {
76 return String::from(s);
77 }
78 String::from("Failed to stringify")
79}
80
81impl std::fmt::Debug for Error {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 if !self.context.is_empty() {
84 f.write_fmt(format_args!("\"{}\"", &self.context))?;
85 }
86 match (&self.inner, self.context.is_empty()) {
87 (InnerError::None, true) => {
88 f.write_str("Error")?;
89 }
90 (InnerError::None, false) => {}
91 (InnerError::Custom(e), _) => {
92 f.write_str(": ")?;
93 std::fmt::Debug::fmt(e, f)?;
94 }
95 #[cfg(target_arch = "wasm32")]
96 (InnerError::JsValue(j), true) => {
97 f.write_str("JS-Error: ")?;
98 f.write_str(&strigify_js(j))?;
99 }
100 #[cfg(target_arch = "wasm32")]
101 (InnerError::JsValue(j), false) => {
102 f.write_str(": ")?;
103 f.write_str(&strigify_js(j))?;
104 }
105 }
106
107 #[cfg(debug_assertions)]
108 {
109 f.write_str(" at ")?;
110 f.write_fmt(format_args!("{}", self.caller))?;
111 }
112
113 Ok(())
114 }
115}
116
117impl std::fmt::Display for Error {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(
120 f,
121 "{}",
122 match self.context.is_empty() {
123 true => match self.inner {
124 InnerError::None => "None Value",
125 InnerError::Custom(_) => "Error",
126 #[cfg(target_arch = "wasm32")]
127 InnerError::JsValue(_) => "JS-Error",
128 },
129 false => self.context(),
130 }
131 )
132 }
133}
134
135impl std::error::Error for Error {}
136
137#[cfg(not(target_arch = "wasm32"))]
138trait CustomError: std::fmt::Debug + std::fmt::Display + Send + Sync + 'static {}
139#[cfg(not(target_arch = "wasm32"))]
140impl<T> CustomError for T where T: std::fmt::Debug + Send + Sync + std::fmt::Display + 'static {}
141
142#[cfg(target_arch = "wasm32")]
143trait CustomError: std::fmt::Debug + std::fmt::Display + 'static {}
144#[cfg(target_arch = "wasm32")]
145impl<T> CustomError for T where T: std::fmt::Debug + std::fmt::Display + 'static {}
146
147#[derive(Debug)]
148enum InnerError {
149 None,
150 #[cfg(target_arch = "wasm32")]
151 JsValue(JsValue),
152 Custom(Box<dyn CustomError>),
153}
154
155pub trait FromCustom
156where
157 Self: Sized,
158{
159 #[track_caller]
160 fn with_context(self, context: impl std::fmt::Display) -> Error;
161}
162
163impl<E> FromCustom for E
164where
165 E: CustomError,
166{
167 #[track_caller]
168 fn with_context(self, context: impl std::fmt::Display) -> Error {
169 Error::new_inner(context, InnerError::Custom(Box::new(self)))
170 }
171}
172
173pub trait FromCustomError<T>
174where
175 Self: Sized,
176{
177 #[track_caller]
178 fn context(self, context: impl std::fmt::Display) -> Result<T>;
179}
180
181impl<T, E> FromCustomError<T> for std::result::Result<T, E>
182where
183 E: CustomError,
184{
185 #[track_caller]
186 fn context(self, context: impl std::fmt::Display) -> Result<T> {
187 self.map_err(|e| e.with_context(context))
188 }
189}
190
191impl<T> FromCustomError<T> for Option<T> {
192 #[track_caller]
193 fn context(self, context: impl std::fmt::Display) -> Result<T> {
194 self.ok_or(Error::new_inner(context, InnerError::None))
195 }
196}
197
198pub trait FromJsValue<T>
199where
200 Self: Sized,
201{
202 #[track_caller]
203 fn context(self, context: impl std::fmt::Display) -> Result<T>;
204}
205
206impl<T> FromJsValue<T> for std::result::Result<T, JsValue> {
207 #[track_caller]
208 fn context(self, context: impl std::fmt::Display) -> Result<T> {
209 #[cfg(not(target_arch = "wasm32"))]
210 unreachable!("JsValue cannot be constructed outside wasm32: {}", context);
211
212 #[cfg(target_arch = "wasm32")]
213 self.map_err(|e| Error::new_inner(context, InnerError::JsValue(e)))
214 }
215}
216
217#[cfg(target_arch = "wasm32")]
218impl From<JsValue> for Error {
219 #[track_caller]
220 fn from(value: JsValue) -> Self {
221 Error::new_inner("Unreconverable JS-Error", InnerError::JsValue(value))
222 }
223}
224
225#[cfg(not(target_arch = "wasm32"))]
226impl From<JsValue> for Error {
227 fn from(_: JsValue) -> Self {
228 unreachable!("JsValue cannot be constructed outside wasm32");
229 }
230}