1use wasm_bindgen::prelude::*;
4
5#[doc(hidden)]
6pub mod _private {
7 pub use js_sys;
8 pub use serde_wasm_bindgen;
9 pub use wasm_bindgen;
10}
11
12#[macro_export]
14macro_rules! wasm_error {
15 (
16 $(#[$meta:meta])*
17 $vis:vis enum $name:ident {
18 $(
19 $(#[$vmeta:meta])*
20 $variant:ident($inner:ty) => $js_name:literal
21 ),+ $(,)?
22 }
23 ) => {
24 $(#[$meta])*
25 $vis enum $name {
26 $( $(#[$vmeta])* $variant($inner), )+
27 }
28
29 impl ::std::fmt::Display for $name {
30 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
31 match self {
32 $( Self::$variant(msg) => write!(f, "{msg}"), )+
33 }
34 }
35 }
36
37 impl From<$name> for $crate::wasm::_private::wasm_bindgen::JsValue {
38 fn from(err: $name) -> Self {
39 let name = match &err {
40 $( $name::$variant(_) => $js_name, )+
41 };
42 let js_err = $crate::wasm::_private::js_sys::Error::new(&err.to_string());
43 js_err.set_name(name);
44 js_err.into()
45 }
46 }
47
48 impl From<$crate::wasm::_private::serde_wasm_bindgen::Error> for $name {
49 fn from(e: $crate::wasm::_private::serde_wasm_bindgen::Error) -> Self {
50 Self::Serialize(e.to_string())
51 }
52 }
53 };
54}
55
56wasm_error! {
57 #[derive(Debug)]
58 #[non_exhaustive]
59 pub enum WasmCommonError {
61 Parse(String) => "ParseError",
62 Decode(String) => "DecodeError",
63 Serialize(String) => "SerializeError",
64 Config(String) => "ConfigError",
65 }
66}
67
68pub fn js_set(obj: &js_sys::Object, key: &str, value: &JsValue) {
69 let _ = js_sys::Reflect::set(obj, &JsValue::from_str(key), value);
70}
71
72pub fn from_js<T>(val: JsValue) -> Result<T, WasmCommonError>
78where
79 T: serde::de::DeserializeOwned,
80{
81 serde_wasm_bindgen::from_value(val).map_err(|e| WasmCommonError::Decode(e.to_string()))
82}
83
84pub fn to_js<T>(val: &T) -> Result<JsValue, serde_wasm_bindgen::Error>
90where
91 T: serde::Serialize,
92{
93 serde_wasm_bindgen::to_value(val)
94}
95
96const HEX_CHARS_PER_BYTE: usize = 2;
97const HEX_RADIX: u32 = 16;
98const HEX_HIGH_NIBBLE_SHIFT: u32 = 4;
99
100pub fn decode_hex_to_bytes(hex: &str) -> Result<Vec<u8>, WasmCommonError> {
106 let hex = hex.strip_prefix("\\x").unwrap_or(hex);
107
108 if hex.len() % HEX_CHARS_PER_BYTE != 0 {
109 return Err(WasmCommonError::Decode("invalid hex length".into()));
110 }
111
112 let mut bytes = Vec::with_capacity(hex.len() / HEX_CHARS_PER_BYTE);
113 let chars: Vec<char> = hex.chars().collect();
114
115 for i in (0..chars.len()).step_by(HEX_CHARS_PER_BYTE) {
116 let hi = chars
117 .get(i)
118 .and_then(|c| c.to_digit(HEX_RADIX))
119 .ok_or_else(|| WasmCommonError::Decode("invalid hex digit".into()))?;
120 let lo = chars
121 .get(i + 1)
122 .and_then(|c| c.to_digit(HEX_RADIX))
123 .ok_or_else(|| WasmCommonError::Decode("invalid hex digit".into()))?;
124 let byte = (hi << HEX_HIGH_NIBBLE_SHIFT) | lo;
125
126 bytes.push(u8::try_from(byte).unwrap_or_default());
127 }
128
129 Ok(bytes)
130}