wasm_bridge_js/conversions/
from_js_value.rs1use anyhow::Context;
2use js_sys::Reflect;
3use wasm_bindgen::{convert::FromWasmAbi, JsValue};
4
5use crate::{
6 helpers::{map_js_error, static_str_to_js},
7 *,
8};
9
10pub trait FromJsValue: Sized {
11 type WasmAbi: FromWasmAbi;
12
13 fn from_js_value(value: &JsValue) -> Result<Self>;
14
15 fn from_fn_result(result: &Result<JsValue, JsValue>) -> Result<Self> {
17 Self::from_js_value(
18 result
19 .as_ref()
20 .map_err(map_js_error("Exported function threw an exception"))?,
21 )
22 }
23
24 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self>;
26}
27
28impl FromJsValue for () {
29 type WasmAbi = JsValue;
30
31 fn from_js_value(value: &JsValue) -> Result<Self> {
32 if value.is_undefined() || value.is_null() {
33 Ok(())
34 } else {
35 Err(map_js_error("Expected null or undefined")(value))
36 }
37 }
38
39 fn from_wasm_abi(_abi: Self::WasmAbi) -> Result<Self> {
40 Ok(())
41 }
42}
43
44impl FromJsValue for bool {
45 type WasmAbi = Self;
46
47 fn from_js_value(value: &JsValue) -> Result<Self> {
48 match value.as_bool() {
49 Some(value) => Ok(value),
50 None => Err(map_js_error("Expected a boolean value")(value)),
51 }
52 }
53
54 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
55 Ok(abi)
56 }
57}
58
59macro_rules! from_js_value_signed {
60 ($name: ty) => {
61 impl FromJsValue for $name {
62 type WasmAbi = Self;
63
64 fn from_js_value(value: &JsValue) -> Result<Self> {
65 match value.as_f64() {
66 Some(number) => Ok(number as _),
67 None => Err(map_js_error("Expected a number")(value)),
68 }
69 }
70
71 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
72 Ok(abi)
73 }
74 }
75 };
76}
77
78from_js_value_signed!(i8);
79from_js_value_signed!(i16);
80from_js_value_signed!(i32);
81
82impl FromJsValue for i64 {
83 type WasmAbi = Self;
84
85 fn from_js_value(value: &JsValue) -> Result<Self> {
86 value
87 .clone()
88 .try_into()
89 .map_err(map_js_error("Expected a bigint"))
90 }
91
92 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
93 Ok(abi)
94 }
95}
96macro_rules! from_js_value_unsigned {
97 ($name: ty, $signed: ty) => {
98 impl FromJsValue for $name {
99 type WasmAbi = Self;
100
101 fn from_js_value(value: &JsValue) -> Result<Self> {
102 match value.as_f64() {
104 Some(number) if number < 0.0 => Ok(number as $signed as _),
106 Some(number) => Ok(number as _),
107 None => Err(map_js_error("Expected a number")(value)),
108 }
109 }
110
111 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
112 Ok(abi)
113 }
114 }
115 };
116}
117
118from_js_value_unsigned!(u8, i8);
119from_js_value_unsigned!(u16, i16);
120from_js_value_unsigned!(u32, i32);
121
122impl FromJsValue for u64 {
123 type WasmAbi = Self;
124
125 fn from_js_value(value: &JsValue) -> Result<Self> {
126 u64::try_from(value.clone())
128 .or_else(|_| i64::try_from(value.clone()).map(|value| value as u64))
129 .map_err(map_js_error("Expected a bigint"))
130 }
131
132 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
133 Ok(abi)
134 }
135}
136
137from_js_value_signed!(f32);
139from_js_value_signed!(f64);
140
141impl FromJsValue for char {
142 type WasmAbi = Self;
143
144 fn from_js_value(value: &JsValue) -> Result<Self> {
145 match value.as_string() {
146 Some(text) if !text.is_empty() => Ok(text.chars().next().unwrap()),
147 _ => Err(map_js_error("Expected a single-character string")(value)),
148 }
149 }
150
151 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
152 Ok(abi)
153 }
154}
155
156impl FromJsValue for String {
157 type WasmAbi = Self;
158
159 fn from_js_value(value: &JsValue) -> Result<Self, crate::Error> {
160 match value.as_string() {
161 Some(value) => Ok(value),
162 None => Err(map_js_error("Expected a string")(value)),
163 }
164 }
165
166 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
167 Ok(abi)
168 }
169}
170
171impl<T: FromJsValue> FromJsValue for Option<T> {
172 type WasmAbi = JsValue; fn from_js_value(value: &JsValue) -> Result<Self> {
175 if value.is_undefined() || value.is_null() {
176 Ok(None)
177 } else {
178 Ok(Some(T::from_js_value(value)?))
179 }
180 }
181
182 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
183 Self::from_js_value(&abi)
184 }
185}
186
187impl<T: FromJsValue, E: FromJsValue> FromJsValue for Result<T, E> {
188 type WasmAbi = JsValue;
189
190 fn from_js_value(value: &JsValue) -> Result<Self> {
191 let tag = Reflect::get(value, static_str_to_js("tag"))
193 .map_err(map_js_error("Get tag from result"))?
194 .as_string()
195 .context("Result tag should be string")?;
196
197 let val = Reflect::get(value, static_str_to_js("val"))
198 .map_err(map_js_error("Get val from result"))?;
199
200 if tag == "ok" {
201 Ok(Ok(T::from_js_value(&val)?))
202 } else if tag == "err" {
203 Ok(Err(E::from_js_value(&val)?))
204 } else {
205 Err(map_js_error("Unknown result tag")(value))
206 }
207 }
208
209 fn from_fn_result(result: &Result<JsValue, JsValue>) -> Result<Self> {
210 Ok(match result {
211 Ok(val) => Ok(T::from_js_value(val)?),
212 Err(err) => {
213 let payload = Reflect::get(err, static_str_to_js("payload"))
214 .map_err(map_js_error("Get result error payload"))?;
215 Err(E::from_js_value(&payload)?)
216 }
217 })
218 }
219
220 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
221 Self::from_js_value(&abi)
222 }
223}
224
225impl<T: FromJsValue> FromJsValue for Vec<T> {
226 type WasmAbi = JsValue;
227
228 fn from_js_value(value: &JsValue) -> Result<Self> {
229 let length = Reflect::get(value, static_str_to_js("length"))
230 .map_err(map_js_error("Get length of array"))?
231 .as_f64()
232 .context("Array length should be a number")? as u32;
233
234 let mut result = Vec::with_capacity(length as usize);
235
236 for index in 0..length {
237 let item = Reflect::get_u32(value, index)
238 .map_err(map_js_error("Get array value at an index"))?;
239 result.push(T::from_js_value(&item)?);
240 }
241
242 Ok(result)
243 }
244
245 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
246 Self::from_js_value(&abi)
247 }
248}
249
250impl FromJsValue for JsValue {
251 type WasmAbi = Self;
252
253 fn from_js_value(value: &JsValue) -> Result<Self> {
254 Ok(value.clone())
255 }
256
257 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
258 Ok(abi)
259 }
260}
261
262impl FromJsValue for Val {
263 type WasmAbi = JsValue;
264
265 fn from_js_value(value: &JsValue) -> Result<Self> {
266 if let Some(number) = value.as_f64() {
267 Ok(number.into())
269 } else if value.is_bigint() {
270 Ok(Val::I64(u64::from_js_value(value)? as _))
272 } else {
273 Err(map_js_error("Unsupported 'Val' value")(value))
274 }
275 }
276
277 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
278 Self::from_js_value(&abi)
279 }
280}
281
282impl<T: FromJsValue> FromJsValue for (T,) {
283 type WasmAbi = T::WasmAbi;
284
285 fn from_js_value(value: &JsValue) -> Result<Self> {
286 Ok((T::from_js_value(value)?,))
287 }
288
289 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
290 Ok((T::from_wasm_abi(abi)?,))
291 }
292
293 fn from_fn_result(result: &Result<JsValue, JsValue>) -> Result<Self> {
294 Ok((T::from_fn_result(result)?,))
295 }
296}
297
298macro_rules! from_js_value_many {
299 ($(($index: tt, $name: ident)),*) => {
300 impl<$($name: FromJsValue),*> FromJsValue for ($($name, )*) {
301 type WasmAbi = JsValue;
302
303 fn from_js_value(results: &JsValue) -> Result<Self> {
304 Ok(( $($name::from_js_value(&Reflect::get_u32(results, $index).map_err(map_js_error("Get tuple value"))?)?,)* ))
305 }
306
307 fn from_wasm_abi(abi: Self::WasmAbi) -> Result<Self> {
308 Self::from_js_value(&abi)
309 }
310 }
311 };
312}
313
314#[rustfmt::skip]
315from_js_value_many!((0, T0), (1, T1));
316#[rustfmt::skip]
317from_js_value_many!((0, T0), (1, T1), (2, T2));
318#[rustfmt::skip]
319from_js_value_many!((0, T0), (1, T1), (2, T2), (3, T3));
320#[rustfmt::skip]
321from_js_value_many!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4));
322#[rustfmt::skip]
323from_js_value_many!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5));
324#[rustfmt::skip]
325from_js_value_many!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5), (6, T6));
326#[rustfmt::skip]
327from_js_value_many!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5), (6, T6), (7, T7));
328
329