1use core::ops::{Deref, DerefMut};
2
3use crate::convert::JsGeneric;
4use crate::sys::JsNullable;
5use crate::{JsCast, JsValue};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum Nullable<T> {
38 Null,
40 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 #[inline]
54 pub const fn is_null(&self) -> bool {
55 matches!(self, Nullable::Null)
56 }
57
58 #[inline]
60 pub const fn is_value(&self) -> bool {
61 matches!(self, Nullable::Value(_))
62 }
63
64 #[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 #[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 #[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 #[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 #[inline]
103 pub fn into_option(self) -> Option<T> {
104 self.value()
105 }
106
107 #[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 #[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 #[inline]
137 pub fn unwrap_or(self, default: T) -> T {
138 self.value().unwrap_or(default)
139 }
140
141 #[inline]
143 pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
144 self.value().unwrap_or_else(f)
145 }
146
147 #[inline]
149 pub fn unwrap_or_default(self) -> T
150 where
151 T: Default,
152 {
153 self.value().unwrap_or_default()
154 }
155
156 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
213 pub fn take(&mut self) -> Nullable<T> {
214 core::mem::replace(self, Nullable::Null)
215 }
216
217 #[inline]
219 pub fn replace(&mut self, value: T) -> Nullable<T> {
220 core::mem::replace(self, Nullable::Value(value))
221 }
222
223 #[inline]
225 pub fn iter(&self) -> core::option::IntoIter<&T> {
226 self.as_ref().value().into_iter()
227 }
228
229 #[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 #[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 #[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 #[inline]
257 pub fn copied(self) -> Nullable<T> {
258 self.map(|&v| v)
259 }
260}
261
262impl<T: Clone> Nullable<&T> {
263 #[inline]
265 pub fn cloned(self) -> Nullable<T> {
266 self.map(|v| v.clone())
267 }
268}
269
270impl<T: Copy> Nullable<&mut T> {
271 #[inline]
273 pub fn copied(self) -> Nullable<T> {
274 self.map(|&mut v| v)
275 }
276}
277
278impl<T: Clone> Nullable<&mut T> {
279 #[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#[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
416impl<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}