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