Skip to main content

wry_bindgen/
sys.rs

1//! JavaScript system value wrappers and promise compatibility traits.
2
3use core::{fmt, marker::PhantomData, ops::Deref};
4
5use crate::{
6    __rt::marker::ErasableGeneric, IntoJsGeneric, JsCast, JsError, JsGeneric, JsValue,
7    convert::UpcastFrom,
8};
9
10/// Marker trait for values that are either a resolution value or a promise-like value.
11pub trait Promising {
12    type Resolution;
13}
14
15macro_rules! js_primitive {
16        ($name:ident, $const_name:ident, $value:expr, $display:literal, $check:expr) => {
17            #[derive(Clone, PartialEq)]
18            #[repr(transparent)]
19            pub struct $name {
20                pub obj: JsValue,
21            }
22
23            impl $name {
24                pub const $const_name: $name = Self { obj: $value };
25            }
26
27            impl Eq for $name {}
28
29            impl Default for $name {
30                fn default() -> Self {
31                    Self::$const_name
32                }
33            }
34
35            impl fmt::Debug for $name {
36                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37                    f.write_str($display)
38                }
39            }
40
41            impl fmt::Display for $name {
42                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43                    f.write_str($display)
44                }
45            }
46
47            impl AsRef<JsValue> for $name {
48                fn as_ref(&self) -> &JsValue {
49                    &self.obj
50                }
51            }
52
53            impl From<$name> for JsValue {
54                fn from(value: $name) -> JsValue {
55                    value.obj
56                }
57            }
58
59            impl From<JsValue> for $name {
60                fn from(obj: JsValue) -> Self {
61                    Self { obj }
62                }
63            }
64
65            impl JsCast for $name {
66                fn instanceof(value: &JsValue) -> bool {
67                    $check(value)
68                }
69
70                fn is_type_of(value: &JsValue) -> bool {
71                    $check(value)
72                }
73
74                fn unchecked_from_js(value: JsValue) -> Self {
75                    Self { obj: value }
76                }
77
78                fn unchecked_from_js_ref(value: &JsValue) -> &Self {
79                    unsafe { &*(value as *const JsValue as *const Self) }
80                }
81            }
82
83            impl_js_value_wire!(for $name, field obj);
84
85            unsafe impl crate::__rt::marker::ErasableGeneric for $name {
86                type Repr = JsValue;
87            }
88
89            impl IntoJsGeneric for $name {
90                type JsCanon = Self;
91
92                fn to_js(self) -> Self {
93                    self
94                }
95            }
96
97            impl UpcastFrom<$name> for $name {}
98            impl UpcastFrom<$name> for JsValue {}
99        };
100    }
101
102js_primitive!(
103    Undefined,
104    UNDEFINED,
105    JsValue::UNDEFINED,
106    "undefined",
107    JsValue::is_undefined
108);
109js_primitive!(Null, NULL, JsValue::NULL, "null", JsValue::is_null);
110
111impl UpcastFrom<()> for Undefined {}
112impl UpcastFrom<Undefined> for () {}
113impl UpcastFrom<()> for JsValue {}
114impl UpcastFrom<()> for () {}
115
116#[derive(Clone, PartialEq)]
117#[repr(transparent)]
118pub struct JsOption<T = JsValue> {
119    pub obj: JsValue,
120    pub generics: PhantomData<fn() -> T>,
121}
122
123impl<T: JsGeneric> JsOption<T> {
124    #[inline]
125    pub fn new() -> Self {
126        Undefined::UNDEFINED.unchecked_into()
127    }
128
129    #[inline]
130    pub fn wrap(val: T) -> Self {
131        val.unchecked_into()
132    }
133
134    #[inline]
135    pub fn from_option(opt: Option<T>) -> Self {
136        match opt {
137            Some(value) => Self::wrap(value),
138            None => Self::new(),
139        }
140    }
141
142    #[inline]
143    pub fn is_empty(&self) -> bool {
144        self.obj.is_null() || self.obj.is_undefined()
145    }
146
147    #[inline]
148    pub fn as_option(&self) -> Option<T> {
149        if self.is_empty() {
150            None
151        } else {
152            Some(T::unchecked_from_js(self.obj.clone()))
153        }
154    }
155
156    #[inline]
157    pub fn into_option(self) -> Option<T> {
158        if self.is_empty() {
159            None
160        } else {
161            Some(self.unchecked_into())
162        }
163    }
164
165    #[inline]
166    pub fn unwrap(self) -> T {
167        self.expect("called `JsOption::unwrap()` on an empty value")
168    }
169
170    #[inline]
171    pub fn expect(self, msg: &str) -> T {
172        match self.into_option() {
173            Some(value) => value,
174            None => panic!("{}", msg),
175        }
176    }
177
178    #[inline]
179    pub fn unwrap_or_default(self) -> T
180    where
181        T: Default,
182    {
183        self.into_option().unwrap_or_default()
184    }
185
186    #[inline]
187    pub fn unwrap_or_else<F>(self, f: F) -> T
188    where
189        F: FnOnce() -> T,
190    {
191        self.into_option().unwrap_or_else(f)
192    }
193}
194
195impl<T: JsGeneric> Default for JsOption<T> {
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201impl<T: JsGeneric + fmt::Debug> fmt::Debug for JsOption<T> {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        write!(f, "{}?(", core::any::type_name::<T>())?;
204        match self.as_option() {
205            Some(value) => write!(f, "{value:?}")?,
206            None => f.write_str("null")?,
207        }
208        f.write_str(")")
209    }
210}
211
212impl<T: JsGeneric + fmt::Display> fmt::Display for JsOption<T> {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        write!(f, "{}?(", core::any::type_name::<T>())?;
215        match self.as_option() {
216            Some(value) => write!(f, "{value}")?,
217            None => f.write_str("null")?,
218        }
219        f.write_str(")")
220    }
221}
222
223impl<T> AsRef<JsValue> for JsOption<T> {
224    fn as_ref(&self) -> &JsValue {
225        &self.obj
226    }
227}
228
229impl<T: JsGeneric> Deref for JsOption<T> {
230    type Target = T;
231
232    fn deref(&self) -> &T {
233        T::unchecked_from_js_ref(&self.obj)
234    }
235}
236
237impl<T> From<JsOption<T>> for JsValue {
238    fn from(value: JsOption<T>) -> JsValue {
239        value.obj
240    }
241}
242
243impl<T> From<JsValue> for JsOption<T> {
244    fn from(obj: JsValue) -> Self {
245        Self {
246            obj,
247            generics: PhantomData,
248        }
249    }
250}
251
252impl<T: JsGeneric> JsCast for JsOption<T> {
253    fn instanceof(value: &JsValue) -> bool {
254        T::is_type_of(value) || value.is_null() || value.is_undefined()
255    }
256
257    fn unchecked_from_js(value: JsValue) -> Self {
258        value.into()
259    }
260
261    fn unchecked_from_js_ref(value: &JsValue) -> &Self {
262        unsafe { &*(value as *const JsValue as *const Self) }
263    }
264}
265
266impl_js_value_wire!(impl<T> for JsOption<T>, field obj);
267
268unsafe impl<T> crate::__rt::marker::ErasableGeneric for JsOption<T> {
269    type Repr = JsValue;
270}
271
272impl<T: JsGeneric> IntoJsGeneric for JsOption<T> {
273    type JsCanon = Self;
274
275    fn to_js(self) -> Self {
276        self
277    }
278}
279
280impl UpcastFrom<JsValue> for JsOption<JsValue> {}
281impl<T> UpcastFrom<Undefined> for JsOption<T> {}
282impl<T> UpcastFrom<Null> for JsOption<T> {}
283impl<T> UpcastFrom<()> for JsOption<T> {}
284impl<T> UpcastFrom<JsOption<T>> for JsValue {}
285impl<T, U> UpcastFrom<JsOption<U>> for JsOption<T> where T: UpcastFrom<U> {}
286
287impl Promising for JsValue {
288    type Resolution = JsValue;
289}
290
291impl Promising for () {
292    type Resolution = Undefined;
293}
294
295macro_rules! promising_self {
296        ($($ty:ty),* $(,)?) => {
297            $(
298                impl Promising for $ty {
299                    type Resolution = $ty;
300                }
301            )*
302        };
303    }
304
305promising_self!(
306    bool, char, f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, JsError
307);
308
309impl<T: Promising> Promising for Option<T> {
310    type Resolution = Option<T::Resolution>;
311}
312
313impl<T: ErasableGeneric + Promising, E: ErasableGeneric> Promising for Result<T, E> {
314    type Resolution = Result<T::Resolution, E>;
315}