Skip to main content

wasm_bindgen/
sys.rs

1//! JavaScript system types that are re-exported by `js-sys`.
2//!
3//! These types represent fundamental JavaScript values and are designed to be
4//! used as generic type parameters in typed JavaScript collections and APIs.
5
6use crate::convert::UpcastFrom;
7use crate::JsCast;
8use crate::JsGeneric;
9use crate::JsValue;
10use core::fmt;
11use core::mem::ManuallyDrop;
12use core::ops::Deref;
13use wasm_bindgen_macro::wasm_bindgen;
14
15/// Marker trait for types which represent `Resolution` or `Promise<Resolution>`.
16///
17/// For all types except for `Promise`, `Resolution` is equal to the type itself.
18/// For `Promise` or any thenable or type extending Promise, `Resolution` is the
19/// type of the promise resolution.
20///
21/// Manually implementing this trait is only required for custom thenables or
22/// types which extend Promise. To disable automatic implementation, use the
23/// `#[wasm_bindgen(no_promising)]` attribute.
24pub trait Promising {
25    /// The type that this value resolves to.
26    type Resolution;
27}
28
29// Undefined
30#[wasm_bindgen(wasm_bindgen = crate)]
31extern "C" {
32    /// The JavaScript `undefined` value.
33    ///
34    /// This type represents the JavaScript `undefined` primitive value and can be
35    /// used as a generic type parameter to indicate that a value is `undefined`.
36    #[wasm_bindgen(is_type_of = JsValue::is_undefined, typescript_type = "undefined", no_upcast)]
37    #[derive(Clone, PartialEq)]
38    pub type Undefined;
39}
40
41impl Undefined {
42    /// The undefined constant.
43    pub const UNDEFINED: Undefined = Self {
44        obj: JsValue::UNDEFINED,
45    };
46}
47
48impl Eq for Undefined {}
49
50impl Default for Undefined {
51    fn default() -> Self {
52        Self::UNDEFINED
53    }
54}
55
56impl fmt::Debug for Undefined {
57    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58        f.write_str("undefined")
59    }
60}
61
62impl fmt::Display for Undefined {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        f.write_str("undefined")
65    }
66}
67
68impl UpcastFrom<Undefined> for Undefined {}
69impl UpcastFrom<()> for Undefined {}
70impl UpcastFrom<Undefined> for () {}
71impl UpcastFrom<Undefined> for JsValue {}
72
73// Null
74#[wasm_bindgen(wasm_bindgen = crate)]
75extern "C" {
76    /// The JavaScript `null` value.
77    ///
78    /// This type represents the JavaScript `null` primitive value and can be
79    /// used as a generic type parameter to indicate that a value is `null`.
80    #[wasm_bindgen(is_type_of = JsValue::is_null, typescript_type = "null", no_upcast)]
81    #[derive(Clone, PartialEq)]
82    pub type Null;
83}
84
85impl Null {
86    /// The null constant.
87    pub const NULL: Null = Self { obj: JsValue::NULL };
88}
89
90impl Eq for Null {}
91
92impl Default for Null {
93    fn default() -> Self {
94        Self::NULL
95    }
96}
97
98impl fmt::Debug for Null {
99    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100        f.write_str("null")
101    }
102}
103
104impl fmt::Display for Null {
105    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        f.write_str("null")
107    }
108}
109
110impl UpcastFrom<Null> for Null {}
111impl UpcastFrom<Null> for JsValue {}
112
113// JsOption
114#[wasm_bindgen(wasm_bindgen = crate)]
115extern "C" {
116    /// A nullable JS value of type `T`.
117    ///
118    /// Unlike `Option<T>`, which is a Rust-side construct, `JsOption<T>` represents
119    /// a JS value that may be `T`, `null`, or `undefined`, where the null status is
120    /// not yet known in Rust. The value remains in JS until inspected via methods
121    /// like [`is_empty`](Self::is_empty), [`as_option`](Self::as_option), or
122    /// [`into_option`](Self::into_option).
123    ///
124    /// `T` must implement [`JsGeneric`], meaning it is any type that can be
125    /// represented as a `JsValue` (e.g., `JsString`, `Number`, `Object`, etc.).
126    /// `JsOption<T>` itself implements `JsGeneric`, so it can be used in all
127    /// generic positions that accept JS types.
128    #[wasm_bindgen(typescript_type = "any", no_upcast)]
129    #[derive(Clone, PartialEq)]
130    pub type JsOption<T>;
131}
132
133impl<T> JsOption<T> {
134    /// Creates an empty `JsOption<T>` representing `null`.
135    #[inline]
136    pub fn new() -> Self {
137        Null::NULL.unchecked_into()
138    }
139
140    /// Wraps a value in a `JsOption<T>`.
141    #[inline]
142    pub fn wrap(val: T) -> Self {
143        unsafe { core::mem::transmute_copy(&ManuallyDrop::new(val)) }
144    }
145
146    /// Creates a `JsOption<T>` from an `Option<T>`.
147    ///
148    /// Returns `JsOption::wrap(val)` if `Some(val)`, otherwise `JsOption::new()`.
149    #[inline]
150    pub fn from_option(opt: Option<T>) -> Self {
151        match opt {
152            Some(val) => Self::wrap(val),
153            None => Self::new(),
154        }
155    }
156
157    /// Tests whether this `JsOption<T>` is empty (`null` or `undefined`).
158    #[inline]
159    pub fn is_empty(&self) -> bool {
160        JsValue::is_null_or_undefined(self)
161    }
162
163    /// Converts this `JsOption<T>` to an `Option<T>` by cloning the inner value.
164    ///
165    /// Returns `None` if the value is `null` or `undefined`, otherwise returns
166    /// `Some(T)` with a clone of the contained value.
167    #[inline]
168    pub fn as_option(&self) -> Option<T> {
169        if JsValue::is_null_or_undefined(self) {
170            None
171        } else {
172            let cloned = self.deref().clone();
173            Some(unsafe { core::mem::transmute_copy(&ManuallyDrop::new(cloned)) })
174        }
175    }
176
177    /// Converts this `JsOption<T>` into an `Option<T>`, consuming `self`.
178    ///
179    /// Returns `None` if the value is `null` or `undefined`, otherwise returns
180    /// `Some(T)` with the contained value.
181    #[inline]
182    pub fn into_option(self) -> Option<T> {
183        if JsValue::is_null_or_undefined(&self) {
184            None
185        } else {
186            Some(unsafe { core::mem::transmute_copy(&ManuallyDrop::new(self)) })
187        }
188    }
189
190    /// Returns the contained value, consuming `self`.
191    ///
192    /// # Panics
193    ///
194    /// Panics if the value is `null` or `undefined`.
195    #[inline]
196    pub fn unwrap(self) -> T {
197        self.expect("called `JsOption::unwrap()` on an empty value")
198    }
199
200    /// Returns the contained value, consuming `self`.
201    ///
202    /// # Panics
203    ///
204    /// Panics if the value is `null` or `undefined`, with a panic message
205    /// including the passed message.
206    #[inline]
207    pub fn expect(self, msg: &str) -> T {
208        match self.into_option() {
209            Some(val) => val,
210            None => panic!("{}", msg),
211        }
212    }
213
214    /// Returns the contained value or a default.
215    ///
216    /// Returns the contained value if not `null` or `undefined`, otherwise
217    /// returns the default value of `T`.
218    #[inline]
219    pub fn unwrap_or_default(self) -> T
220    where
221        T: Default,
222    {
223        self.into_option().unwrap_or_default()
224    }
225
226    /// Returns the contained value or computes it from a closure.
227    ///
228    /// Returns the contained value if not `null` or `undefined`, otherwise
229    /// calls `f` and returns the result.
230    #[inline]
231    pub fn unwrap_or_else<F>(self, f: F) -> T
232    where
233        F: FnOnce() -> T,
234    {
235        self.into_option().unwrap_or_else(f)
236    }
237}
238
239impl<T: JsGeneric> Default for JsOption<T> {
240    fn default() -> Self {
241        Self::new()
242    }
243}
244
245impl<T: JsGeneric + fmt::Debug> fmt::Debug for JsOption<T> {
246    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247        write!(f, "{}?(", core::any::type_name::<T>())?;
248        match self.as_option() {
249            Some(v) => write!(f, "{v:?}")?,
250            None => f.write_str("null")?,
251        }
252        f.write_str(")")
253    }
254}
255
256impl<T: JsGeneric + fmt::Display> fmt::Display for JsOption<T> {
257    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258        write!(f, "{}?(", core::any::type_name::<T>())?;
259        match self.as_option() {
260            Some(v) => write!(f, "{v}")?,
261            None => f.write_str("null")?,
262        }
263        f.write_str(")")
264    }
265}
266
267impl UpcastFrom<JsValue> for JsOption<JsValue> {}
268impl<T> UpcastFrom<Undefined> for JsOption<T> {}
269impl<T> UpcastFrom<Null> for JsOption<T> {}
270impl<T> UpcastFrom<()> for JsOption<T> {}
271impl<T> UpcastFrom<JsOption<T>> for JsValue {}
272impl<T, U> UpcastFrom<JsOption<U>> for JsOption<T> where T: UpcastFrom<U> {}