wry_bindgen/
try_from_js.rs1use alloc::string::{String, ToString};
4use alloc::vec::Vec;
5
6use crate::{JsCast, JsValue, convert};
7
8macro_rules! cast {
9 (($from:ty => $to:ty) $val:expr) => {{ $crate::__rt::wbg_cast::<$from, $to>($val) }};
10}
11
12macro_rules! to_js_value {
13 ($ty:ty) => {
14 impl From<$ty> for $crate::JsValue {
15 fn from(val: $ty) -> Self {
16 cast! {($ty => $crate::JsValue) val}
17 }
18 }
19 };
20}
21
22macro_rules! from_js_value {
23 ($ty:ty) => {
24 impl From<$crate::JsValue> for $ty {
25 fn from(val: $crate::JsValue) -> Self {
26 cast! {($crate::JsValue => $ty) val}
27 }
28 }
29 };
30}
31
32impl TryFrom<JsValue> for u64 {
33 type Error = JsValue;
34
35 fn try_from(v: JsValue) -> Result<Self, JsValue> {
36 <Self as convert::TryFromJsValue>::try_from_js_value(v)
37 }
38}
39
40impl TryFrom<JsValue> for i64 {
41 type Error = JsValue;
42
43 fn try_from(v: JsValue) -> Result<Self, JsValue> {
44 <Self as convert::TryFromJsValue>::try_from_js_value(v)
45 }
46}
47
48impl TryFrom<JsValue> for f64 {
49 type Error = JsValue;
50
51 fn try_from(val: JsValue) -> Result<Self, Self::Error> {
52 val.as_f64().ok_or(val)
53 }
54}
55
56impl TryFrom<&JsValue> for f64 {
57 type Error = JsValue;
58
59 fn try_from(val: &JsValue) -> Result<Self, Self::Error> {
60 val.as_f64().ok_or_else(|| val.clone())
61 }
62}
63
64impl TryFrom<JsValue> for i128 {
65 type Error = JsValue;
66
67 fn try_from(v: JsValue) -> Result<Self, JsValue> {
68 <Self as convert::TryFromJsValue>::try_from_js_value(v)
69 }
70}
71
72impl TryFrom<JsValue> for u128 {
73 type Error = JsValue;
74
75 fn try_from(v: JsValue) -> Result<Self, JsValue> {
76 <Self as convert::TryFromJsValue>::try_from_js_value(v)
77 }
78}
79
80impl TryFrom<JsValue> for String {
81 type Error = JsValue;
82
83 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
84 value.as_string().ok_or(value)
85 }
86}
87
88impl convert::TryFromJsValue for String {
89 fn try_from_js_value_ref(value: &JsValue) -> Option<Self> {
90 value.as_string()
91 }
92}
93
94impl convert::TryFromJsValue for bool {
95 fn try_from_js_value_ref(value: &JsValue) -> Option<Self> {
96 value.as_bool()
97 }
98}
99
100impl convert::TryFromJsValue for char {
101 fn try_from_js_value_ref(value: &JsValue) -> Option<Self> {
102 let s = value.as_string()?;
103 let mut chars = s.chars();
104 let c = chars.next()?;
105 if chars.next().is_none() {
106 Some(c)
107 } else {
108 None
109 }
110 }
111}
112
113impl convert::TryFromJsValue for () {
114 fn try_from_js_value_ref(value: &JsValue) -> Option<Self> {
115 if value.is_undefined() { Some(()) } else { None }
116 }
117}
118
119impl<T: convert::TryFromJsValue> convert::TryFromJsValue for Option<T> {
120 fn try_from_js_value_ref(value: &JsValue) -> Option<Self> {
121 if value.is_undefined() {
122 Some(None)
123 } else {
124 T::try_from_js_value_ref(value).map(Some)
125 }
126 }
127}
128
129impl<T: convert::TryFromJsValue> convert::TryFromJsValue for Vec<T> {
130 fn try_from_js_value_ref(value: &JsValue) -> Option<Self> {
131 if !value.is_array() {
132 return None;
133 }
134 let length = crate::js_helpers::js_reflect_get(value, &JsValue::from_str("length"));
135 let len = length.as_f64()? as u32;
136 let mut out = Vec::with_capacity(len as usize);
137 for i in 0..len {
138 let element = crate::js_helpers::js_reflect_get(value, &JsValue::from_f64(i as f64));
139 out.push(T::try_from_js_value(element).ok()?);
140 }
141 Some(out)
142 }
143}
144
145fn js_number_is_integer_in_range(number: f64, min: f64, max: f64) -> bool {
146 number.is_finite() && number.fract() == 0.0 && (min..=max).contains(&number)
147}
148
149macro_rules! try_from_js_value_signed_int {
150 ($($ty:ty),* $(,)?) => {
151 $(
152 impl convert::TryFromJsValue for $ty {
153 fn try_from_js_value_ref(val: &JsValue) -> Option<$ty> {
154 let number = val.as_f64()?;
155 if js_number_is_integer_in_range(number, <$ty>::MIN as f64, <$ty>::MAX as f64) {
156 Some(number as $ty)
157 } else {
158 None
159 }
160 }
161 }
162 )*
163 };
164}
165
166macro_rules! try_from_js_value_unsigned_int {
167 ($($ty:ty),* $(,)?) => {
168 $(
169 impl convert::TryFromJsValue for $ty {
170 fn try_from_js_value_ref(val: &JsValue) -> Option<$ty> {
171 let number = val.as_f64()?;
172 if js_number_is_integer_in_range(number, 0.0, <$ty>::MAX as f64) {
173 Some(number as $ty)
174 } else {
175 None
176 }
177 }
178 }
179 )*
180 };
181}
182
183try_from_js_value_signed_int!(i8, i16, i32);
184try_from_js_value_unsigned_int!(u8, u16, u32);
185
186impl convert::TryFromJsValue for f32 {
187 fn try_from_js_value_ref(val: &JsValue) -> Option<f32> {
188 val.as_f64().map(|n| n as f32)
189 }
190}
191
192impl convert::TryFromJsValue for f64 {
193 fn try_from_js_value_ref(val: &JsValue) -> Option<f64> {
194 val.as_f64()
195 }
196}
197
198impl convert::TryFromJsValue for i64 {
199 fn try_from_js_value_ref(val: &JsValue) -> Option<i64> {
200 crate::js_helpers::js_bigint_get_as_i64(val)
201 }
202}
203
204impl convert::TryFromJsValue for u64 {
205 fn try_from_js_value_ref(val: &JsValue) -> Option<u64> {
206 crate::js_helpers::js_bigint_to_string(val)?.parse().ok()
207 }
208}
209
210impl convert::TryFromJsValue for i128 {
211 fn try_from_js_value_ref(v: &JsValue) -> Option<i128> {
212 crate::js_helpers::js_bigint_to_string(v)?.parse().ok()
213 }
214}
215
216impl convert::TryFromJsValue for u128 {
217 fn try_from_js_value_ref(v: &JsValue) -> Option<u128> {
218 crate::js_helpers::js_bigint_to_string(v)?.parse().ok()
219 }
220}
221
222impl convert::TryFromJsValue for isize {
223 fn try_from_js_value_ref(val: &JsValue) -> Option<isize> {
224 val.as_f64().map(|n| n as isize)
225 }
226}
227
228impl convert::TryFromJsValue for usize {
229 fn try_from_js_value_ref(val: &JsValue) -> Option<usize> {
230 val.as_f64().map(|n| n as usize)
231 }
232}
233
234to_js_value!(i8);
235from_js_value!(i8);
236to_js_value!(i16);
237from_js_value!(i16);
238to_js_value!(i32);
239from_js_value!(i32);
240to_js_value!(i64);
241to_js_value!(i128);
242to_js_value!(u8);
243from_js_value!(u8);
244to_js_value!(u16);
245from_js_value!(u16);
246to_js_value!(u32);
247from_js_value!(u32);
248to_js_value!(u64);
249to_js_value!(u128);
250to_js_value!(f32);
251from_js_value!(f32);
252to_js_value!(f64);
253to_js_value!(usize);
254from_js_value!(usize);
255to_js_value!(isize);
256from_js_value!(isize);
257impl<'a> From<&'a str> for JsValue {
258 fn from(s: &'a str) -> JsValue {
259 cast! {(String => JsValue) s.to_string()}
260 }
261}
262impl<'a> From<&'a String> for JsValue {
263 fn from(s: &'a String) -> JsValue {
264 cast! {(String => JsValue) s.clone()}
265 }
266}
267impl<'a, T> From<&'a T> for JsValue
268where
269 T: JsCast,
270{
271 fn from(s: &'a T) -> JsValue {
272 s.as_ref().clone()
273 }
274}
275impl From<String> for JsValue {
276 fn from(s: String) -> JsValue {
277 cast! {(String => JsValue) s}
278 }
279}
280to_js_value!(());
281from_js_value!(());
282
283impl<T> From<Option<T>> for JsValue
284where
285 T: Into<JsValue>,
286{
287 fn from(s: Option<T>) -> JsValue {
288 match s {
289 Some(s) => s.into(),
290 None => JsValue::undefined(),
291 }
292 }
293}