Skip to main content

wasm_bindgen/
nullable.rs

1use core::ops::{Deref, DerefMut};
2
3use crate::convert::JsGeneric;
4use crate::sys::JsNullable;
5use crate::{JsCast, JsValue};
6
7/// A `T` or `null` at the JS boundary.
8///
9/// `Nullable<T>` is the Rust-side type for a JS value of type `T | null`, as
10/// `Option<T>` is for `T | undefined`, and `Option<Nullable<T>>` is the union
11/// `T | null | undefined`. All three are accepted in the same positions: as an
12/// argument or return value of exported and imported functions, as a public
13/// field of an exported struct, and as a closure argument or return value,
14/// including the `&`/`&mut` reference forms `Option<T>` supports.
15///
16/// | Type | JS type | JS → Rust | Rust → JS |
17/// |---|---|---|---|
18/// | `Option<T>` | `T \| undefined` | `undefined` and `null` → `None`; anything else goes to `T`'s conversion | `None` → `undefined` |
19/// | `Nullable<T>` | `T \| null` | `null` → `Null`; anything else, `undefined` included, goes to `T`'s conversion | `Null` → `null` |
20/// | `Option<Nullable<T>>` | `T \| null \| undefined` | `undefined` → `None`, `null` → `Some(Null)`, else `Some(Value(..))` | `None` → `undefined`, `Some(Null)` → `null` |
21///
22/// The `Option` layer of `Option<Nullable<T>>` treats only `undefined` as
23/// `None`, since `null` has to reach the `Nullable` layer.
24/// `opt.and_then(Nullable::value)` collapses the union to an
25/// `Option<T>`. No other nesting is supported: `Nullable<Nullable<T>>`,
26/// `Nullable<Option<T>>` and `Option<Option<T>>` do not cross the boundary.
27///
28/// Both may also name a slot of a js-sys `Function<fn(..)>` signature or a
29/// `Promise<T>` resolution. Where a js-sys container's type-erased methods
30/// marshal the element itself, as `Array::get` and `Map::get` do, the
31/// element must be a handle: use the JS-side [`JsOption<T>`] and
32/// [`JsNullable<T>`] there, which stay JS values rather than converting.
33///
34/// [`JsOption<T>`]: crate::sys::JsOption
35/// [`JsNullable<T>`]: crate::sys::JsNullable
36#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum Nullable<T> {
38    /// JS `null`.
39    Null,
40    /// A value.
41    Value(T),
42}
43
44impl<T> Default for Nullable<T> {
45    #[inline]
46    fn default() -> Self {
47        Nullable::Null
48    }
49}
50
51impl<T> Nullable<T> {
52    /// Returns `true` if this is `Null`.
53    #[inline]
54    pub const fn is_null(&self) -> bool {
55        matches!(self, Nullable::Null)
56    }
57
58    /// Returns `true` if this is a `Value`.
59    #[inline]
60    pub const fn is_value(&self) -> bool {
61        matches!(self, Nullable::Value(_))
62    }
63
64    /// Converts from `&Nullable<T>` to `Nullable<&T>`.
65    #[inline]
66    pub const fn as_ref(&self) -> Nullable<&T> {
67        match self {
68            Nullable::Null => Nullable::Null,
69            Nullable::Value(v) => Nullable::Value(v),
70        }
71    }
72
73    /// Converts from `&mut Nullable<T>` to `Nullable<&mut T>`.
74    #[inline]
75    pub fn as_mut(&mut self) -> Nullable<&mut T> {
76        match self {
77            Nullable::Null => Nullable::Null,
78            Nullable::Value(v) => Nullable::Value(v),
79        }
80    }
81
82    /// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a
83    /// contained value, leaving `Null` as it is.
84    #[inline]
85    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
86        match self {
87            Nullable::Null => Nullable::Null,
88            Nullable::Value(v) => Nullable::Value(f(v)),
89        }
90    }
91
92    /// Returns the contained value as `Some`, or `None` for `Null`.
93    #[inline]
94    pub fn value(self) -> Option<T> {
95        match self {
96            Nullable::Value(v) => Some(v),
97            Nullable::Null => None,
98        }
99    }
100
101    /// Returns the contained value as `Some`, or `None` for `Null`.
102    #[inline]
103    pub fn into_option(self) -> Option<T> {
104        self.value()
105    }
106
107    /// Returns the contained value.
108    ///
109    /// # Panics
110    ///
111    /// Panics if this is `Null`.
112    #[inline]
113    #[track_caller]
114    pub fn unwrap(self) -> T {
115        match self {
116            Nullable::Value(v) => v,
117            Nullable::Null => panic!("called `Nullable::unwrap()` on a `Null` value"),
118        }
119    }
120
121    /// Returns the contained value.
122    ///
123    /// # Panics
124    ///
125    /// Panics with `msg` if this is `Null`.
126    #[inline]
127    #[track_caller]
128    pub fn expect(self, msg: &str) -> T {
129        match self {
130            Nullable::Value(v) => v,
131            Nullable::Null => panic!("{}", msg),
132        }
133    }
134
135    /// Returns the contained value or `default`.
136    #[inline]
137    pub fn unwrap_or(self, default: T) -> T {
138        self.value().unwrap_or(default)
139    }
140
141    /// Returns the contained value or computes it from `f`.
142    #[inline]
143    pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
144        self.value().unwrap_or_else(f)
145    }
146
147    /// Returns the contained value or `T::default()`.
148    #[inline]
149    pub fn unwrap_or_default(self) -> T
150    where
151        T: Default,
152    {
153        self.value().unwrap_or_default()
154    }
155
156    /// Returns `true` if this is a `Value` and `f` holds for it.
157    #[inline]
158    pub fn is_value_and(self, f: impl FnOnce(T) -> bool) -> bool {
159        match self {
160            Nullable::Value(v) => f(v),
161            Nullable::Null => false,
162        }
163    }
164
165    /// Returns `true` if this is `Null`, or a `Value` for which `f` holds.
166    #[inline]
167    pub fn is_null_or(self, f: impl FnOnce(T) -> bool) -> bool {
168        match self {
169            Nullable::Value(v) => f(v),
170            Nullable::Null => true,
171        }
172    }
173
174    /// Returns the contained value as `Ok`, or `Err(err)` for `Null`.
175    #[inline]
176    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
177        match self {
178            Nullable::Value(v) => Ok(v),
179            Nullable::Null => Err(err),
180        }
181    }
182
183    /// Returns the contained value as `Ok`, or `Err(err())` for `Null`.
184    #[inline]
185    pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
186        match self {
187            Nullable::Value(v) => Ok(v),
188            Nullable::Null => Err(err()),
189        }
190    }
191
192    /// Returns `Null` for `Null`, otherwise calls `f` on the contained value.
193    #[inline]
194    pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
195        match self {
196            Nullable::Value(v) => f(v),
197            Nullable::Null => Nullable::Null,
198        }
199    }
200
201    /// Returns the contained value if `predicate` holds for it, otherwise
202    /// `Null`.
203    #[inline]
204    pub fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Nullable<T> {
205        match self {
206            Nullable::Value(v) if predicate(&v) => Nullable::Value(v),
207            _ => Nullable::Null,
208        }
209    }
210
211    /// Takes the value out, leaving `Null` in its place.
212    #[inline]
213    pub fn take(&mut self) -> Nullable<T> {
214        core::mem::replace(self, Nullable::Null)
215    }
216
217    /// Replaces the contents with `Value(value)`, returning the old contents.
218    #[inline]
219    pub fn replace(&mut self, value: T) -> Nullable<T> {
220        core::mem::replace(self, Nullable::Value(value))
221    }
222
223    /// Returns an iterator over the contained value, if any.
224    #[inline]
225    pub fn iter(&self) -> core::option::IntoIter<&T> {
226        self.as_ref().value().into_iter()
227    }
228
229    /// Returns a mutable iterator over the contained value, if any.
230    #[inline]
231    pub fn iter_mut(&mut self) -> core::option::IntoIter<&mut T> {
232        self.as_mut().value().into_iter()
233    }
234}
235
236impl<T: Deref> Nullable<T> {
237    /// Converts from `Nullable<T>` (or `&Nullable<T>`) to
238    /// `Nullable<&T::Target>`.
239    #[inline]
240    pub fn as_deref(&self) -> Nullable<&T::Target> {
241        self.as_ref().map(|v| v.deref())
242    }
243}
244
245impl<T: DerefMut> Nullable<T> {
246    /// Converts from `Nullable<T>` (or `&mut Nullable<T>`) to
247    /// `Nullable<&mut T::Target>`.
248    #[inline]
249    pub fn as_deref_mut(&mut self) -> Nullable<&mut T::Target> {
250        self.as_mut().map(|v| v.deref_mut())
251    }
252}
253
254impl<T: Copy> Nullable<&T> {
255    /// Maps a `Nullable<&T>` to a `Nullable<T>` by copying the referent.
256    #[inline]
257    pub fn copied(self) -> Nullable<T> {
258        self.map(|&v| v)
259    }
260}
261
262impl<T: Clone> Nullable<&T> {
263    /// Maps a `Nullable<&T>` to a `Nullable<T>` by cloning the referent.
264    #[inline]
265    pub fn cloned(self) -> Nullable<T> {
266        self.map(|v| v.clone())
267    }
268}
269
270impl<T: Copy> Nullable<&mut T> {
271    /// Maps a `Nullable<&mut T>` to a `Nullable<T>` by copying the referent.
272    #[inline]
273    pub fn copied(self) -> Nullable<T> {
274        self.map(|&mut v| v)
275    }
276}
277
278impl<T: Clone> Nullable<&mut T> {
279    /// Maps a `Nullable<&mut T>` to a `Nullable<T>` by cloning the referent.
280    #[inline]
281    pub fn cloned(self) -> Nullable<T> {
282        self.map(|v| v.clone())
283    }
284}
285
286impl<T> IntoIterator for Nullable<T> {
287    type Item = T;
288    type IntoIter = core::option::IntoIter<T>;
289
290    #[inline]
291    fn into_iter(self) -> Self::IntoIter {
292        self.value().into_iter()
293    }
294}
295
296impl<'a, T> IntoIterator for &'a Nullable<T> {
297    type Item = &'a T;
298    type IntoIter = core::option::IntoIter<&'a T>;
299
300    #[inline]
301    fn into_iter(self) -> Self::IntoIter {
302        self.iter()
303    }
304}
305
306impl<'a, T> IntoIterator for &'a mut Nullable<T> {
307    type Item = &'a mut T;
308    type IntoIter = core::option::IntoIter<&'a mut T>;
309
310    #[inline]
311    fn into_iter(self) -> Self::IntoIter {
312        self.iter_mut()
313    }
314}
315
316/// `?` on a `Nullable<T>` under `--cfg wasm_bindgen_unstable_try_trait`,
317/// which needs nightly for `try_trait_v2`. `Null` short-circuits a function
318/// returning `Nullable<U>` to `Null` and one returning `Option<U>` to `None`,
319/// and `None` from an `Option<T>` short-circuits a `Nullable<U>` function to
320/// `Null`.
321#[cfg(wasm_bindgen_unstable_try_trait)]
322mod try_trait {
323    use core::convert::Infallible;
324    use core::ops::{ControlFlow, FromResidual, Residual, Try};
325
326    use super::Nullable;
327
328    impl<T> Residual<T> for Nullable<Infallible> {
329        type TryType = Nullable<T>;
330    }
331
332    impl<T> Try for Nullable<T> {
333        type Output = T;
334        type Residual = Nullable<Infallible>;
335
336        #[inline]
337        fn from_output(output: T) -> Self {
338            Nullable::Value(output)
339        }
340
341        #[inline]
342        fn branch(self) -> ControlFlow<Self::Residual, T> {
343            match self {
344                Nullable::Value(v) => ControlFlow::Continue(v),
345                Nullable::Null => ControlFlow::Break(Nullable::Null),
346            }
347        }
348    }
349
350    impl<T> FromResidual<Nullable<Infallible>> for Nullable<T> {
351        #[inline]
352        fn from_residual(_: Nullable<Infallible>) -> Self {
353            Nullable::Null
354        }
355    }
356
357    impl<T> FromResidual<Option<Infallible>> for Nullable<T> {
358        #[inline]
359        fn from_residual(_: Option<Infallible>) -> Self {
360            Nullable::Null
361        }
362    }
363
364    impl<T> FromResidual<Nullable<Infallible>> for Option<T> {
365        #[inline]
366        fn from_residual(_: Nullable<Infallible>) -> Self {
367            None
368        }
369    }
370}
371
372impl<T> From<T> for Nullable<T> {
373    #[inline]
374    fn from(value: T) -> Self {
375        Nullable::Value(value)
376    }
377}
378
379impl<T> From<Option<T>> for Nullable<T> {
380    #[inline]
381    fn from(option: Option<T>) -> Self {
382        match option {
383            Some(v) => Nullable::Value(v),
384            None => Nullable::Null,
385        }
386    }
387}
388
389impl<T> From<Nullable<T>> for Option<T> {
390    #[inline]
391    fn from(nullable: Nullable<T>) -> Self {
392        nullable.value()
393    }
394}
395
396impl<T: Into<JsValue>> From<Nullable<T>> for JsValue {
397    #[inline]
398    fn from(nullable: Nullable<T>) -> Self {
399        match nullable {
400            Nullable::Null => JsValue::NULL,
401            Nullable::Value(v) => v.into(),
402        }
403    }
404}
405
406impl<T: JsGeneric> From<Nullable<T>> for JsNullable<T> {
407    #[inline]
408    fn from(nullable: Nullable<T>) -> Self {
409        match nullable {
410            Nullable::Null => JsNullable::new(),
411            Nullable::Value(v) => JsNullable::wrap(v),
412        }
413    }
414}
415
416/// `None` is `undefined`, which `JsNullable` admits as its other empty state.
417impl<T: JsGeneric> From<Option<Nullable<T>>> for JsNullable<T> {
418    #[inline]
419    fn from(option: Option<Nullable<T>>) -> Self {
420        match option {
421            Some(nullable) => nullable.into(),
422            None => JsValue::UNDEFINED.unchecked_into(),
423        }
424    }
425}
426
427impl<T: JsGeneric> From<JsNullable<T>> for Option<Nullable<T>> {
428    #[inline]
429    fn from(nullable: JsNullable<T>) -> Self {
430        if JsValue::is_undefined(&nullable) {
431            None
432        } else if JsValue::is_null(&nullable) {
433            Some(Nullable::Null)
434        } else {
435            Some(Nullable::Value(nullable.unchecked_into()))
436        }
437    }
438}