1use bytemuck_derive::{Pod, Zeroable};
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
4#[repr(transparent)]
5pub struct PodU16([u8; 2]);
6impl From<u16> for PodU16 {
7 fn from(n: u16) -> Self {
8 Self(n.to_le_bytes())
9 }
10}
11impl From<PodU16> for u16 {
12 fn from(pod: PodU16) -> Self {
13 Self::from_le_bytes(pod.0)
14 }
15}
16
17#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
19#[repr(transparent)]
20pub struct PodU64([u8; 8]);
21impl From<u64> for PodU64 {
22 fn from(n: u64) -> Self {
23 Self(n.to_le_bytes())
24 }
25}
26impl From<PodU64> for u64 {
27 fn from(pod: PodU64) -> Self {
28 Self::from_le_bytes(pod.0)
29 }
30}
31
32macro_rules! impl_from_str {
33 (TYPE = $type:ident, BYTES_LEN = $bytes_len:expr, BASE64_LEN = $base64_len:expr) => {
34 impl std::str::FromStr for $type {
35 type Err = crate::errors::ParseError;
36
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38 if s.len() > $base64_len {
39 return Err(Self::Err::WrongSize);
40 }
41 let mut bytes = [0u8; $bytes_len];
42 let decoded_len = BASE64_STANDARD
43 .decode_slice(s, &mut bytes)
44 .map_err(|_| Self::Err::Invalid)?;
45 if decoded_len != $bytes_len {
46 Err(Self::Err::WrongSize)
47 } else {
48 Ok($type(bytes))
49 }
50 }
51 }
52 };
53}
54pub(crate) use impl_from_str;
55
56macro_rules! impl_from_bytes {
57 (TYPE = $type:ident, BYTES_LEN = $bytes_len:expr) => {
58 impl std::convert::From<[u8; $bytes_len]> for $type {
59 fn from(bytes: [u8; $bytes_len]) -> Self {
60 Self(bytes)
61 }
62 }
63 };
64}
65pub(crate) use impl_from_bytes;
66
67macro_rules! impl_wasm_bindings {
68 (POD_TYPE = $pod_type:ident, DECODED_TYPE = $decoded_type: ident) => {
69 #[cfg(target_arch = "wasm32")]
70 #[allow(non_snake_case)]
71 #[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
72 impl $pod_type {
73 #[wasm_bindgen::prelude::wasm_bindgen(constructor)]
74 pub fn constructor(
75 value: wasm_bindgen::JsValue,
76 ) -> Result<$pod_type, wasm_bindgen::JsValue> {
77 if let Some(base64_str) = value.as_string() {
78 base64_str
79 .parse::<$pod_type>()
80 .map_err(|e| e.to_string().into())
81 } else if let Some(uint8_array) = value.dyn_ref::<js_sys::Uint8Array>() {
82 bytemuck::try_from_bytes(&uint8_array.to_vec())
83 .map_err(|err| {
84 wasm_bindgen::JsValue::from(format!("Invalid Uint8Array: {err:?}"))
85 })
86 .map(|value| *value)
87 } else if let Some(array) = value.dyn_ref::<js_sys::Array>() {
88 let mut bytes = vec![];
89 let iterator =
90 js_sys::try_iter(&array.values())?.expect("array to be iterable");
91 for x in iterator {
92 let x = x?;
93
94 if let Some(n) = x.as_f64() {
95 if (0. ..=255.).contains(&n) {
96 bytes.push(n as u8);
97 continue;
98 }
99 }
100 return Err(format!("Invalid array argument: {:?}", x).into());
101 }
102
103 bytemuck::try_from_bytes(&bytes)
104 .map_err(|err| {
105 wasm_bindgen::JsValue::from(format!("Invalid Array: {err:?}"))
106 })
107 .map(|value| *value)
108 } else if value.is_undefined() {
109 Ok($pod_type::default())
110 } else {
111 Err("Unsupported argument".into())
112 }
113 }
114
115 pub fn toString(&self) -> String {
116 self.to_string()
117 }
118
119 pub fn equals(&self, other: &$pod_type) -> bool {
120 self == other
121 }
122
123 pub fn toBytes(&self) -> Box<[u8]> {
124 self.0.into()
125 }
126
127 pub fn zeroed() -> Self {
128 Self::default()
129 }
130
131 pub fn encode(decoded: &$decoded_type) -> $pod_type {
132 (*decoded).into()
133 }
134
135 pub fn decode(&self) -> Result<$decoded_type, wasm_bindgen::JsValue> {
136 (*self)
137 .try_into()
138 .map_err(|err| JsValue::from(format!("Invalid encoding: {err:?}")))
139 }
140 }
141 };
142}
143pub(crate) use impl_wasm_bindings;