trading_charts/
error.rs

1use js_sys::Reflect;
2use chrono::ParseError;
3use gloo_console::externs::error as console_error;
4use serde_wasm_bindgen::{Error as SerdeError, to_value};
5use wasm_bindgen::JsValue;
6use std::{
7    fmt::{Debug, Display, Formatter, Result as FmtResult},
8    error::Error as StdError,
9};
10
11#[derive(Debug, Clone)]
12pub struct JsError {
13    message: String,
14    prefix:  Option<String>,
15    data:    Option<JsValue>,
16}
17
18impl JsError {
19    pub fn new(js: JsValue) -> Self {
20        let msg_prop_name = JsValue::from_str("message");
21        let msg_prop_value = Reflect::get(&js, &msg_prop_name);
22        let message = match msg_prop_value.as_ref() {
23            Ok(msg) => msg,
24            Err(_) => &js,
25        };
26
27        Self {
28            message: message.as_string().unwrap_or_else(|| format!("{message:?}")),
29            prefix:  None,
30            data:    None,
31        }
32    }
33
34    pub fn from_displayable<E: Display>(err: E) -> Self {
35        Self {
36            message: err.to_string(),
37            prefix:  None,
38            data:    None,
39        }
40    }
41
42    pub fn new_from_str(message: impl AsRef<str>) -> Self {
43        Self {
44            message: message.as_ref().to_string(),
45            prefix:  None,
46            data:    None,
47        }
48    }
49
50    pub fn with_prefix(self, prefix: impl AsRef<str>) -> Self {
51        Self {
52            prefix: Some(prefix.as_ref().to_string()),
53            ..self
54        }
55    }
56
57    pub fn with_data(self, data: JsValue) -> Self {
58        Self {
59            data: Some(data),
60            ..self
61        }
62    }
63
64    pub fn with_serializable_data(self, data: impl serde::Serialize) -> Self {
65        let data =
66            to_value(&data).unwrap_or_else(|err| JsValue::from_str(&format!("Serialization error on data: {err}")));
67
68        self.with_data(data)
69    }
70
71    #[allow(dead_code)]
72    pub fn message(&self) -> &str {
73        &self.message
74    }
75
76    pub fn log(&self) {
77        let msg = match &self.prefix {
78            None => JsValue::from_str(&self.message),
79            Some(prefix) => JsValue::from_str(&format!("{prefix}: {}", self.message)),
80        };
81
82        let mut args = vec![msg];
83        if let Some(data) = &self.data {
84            args.push(JsValue::from_str(" - with data"));
85            args.push(data.clone());
86        };
87
88        console_error(args.into_boxed_slice());
89    }
90}
91
92impl Display for JsError {
93    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
94        <String as Display>::fmt(&self.message, f)
95    }
96}
97
98impl StdError for JsError {}
99
100impl From<JsValue> for JsError {
101    fn from(js: JsValue) -> Self {
102        Self::new(js)
103    }
104}
105
106impl From<SerdeError> for JsError {
107    fn from(err: SerdeError) -> Self {
108        Self {
109            message: format!("JSON error: {err}"),
110            prefix:  None,
111            data:    None,
112        }
113    }
114}
115
116impl From<ParseError> for JsError {
117    fn from(err: ParseError) -> Self {
118        Self {
119            message: format!("Parse error: {err}"),
120            prefix:  None,
121            data:    None,
122        }
123    }
124}