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