use core::ops::{Deref, DerefMut};
use crate::convert::JsGeneric;
use crate::sys::JsNullable;
use crate::{JsCast, JsValue};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Nullable<T> {
Null,
Value(T),
}
impl<T> Default for Nullable<T> {
#[inline]
fn default() -> Self {
Nullable::Null
}
}
impl<T> Nullable<T> {
#[inline]
pub const fn is_null(&self) -> bool {
matches!(self, Nullable::Null)
}
#[inline]
pub const fn is_value(&self) -> bool {
matches!(self, Nullable::Value(_))
}
#[inline]
pub const fn as_ref(&self) -> Nullable<&T> {
match self {
Nullable::Null => Nullable::Null,
Nullable::Value(v) => Nullable::Value(v),
}
}
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match self {
Nullable::Null => Nullable::Null,
Nullable::Value(v) => Nullable::Value(v),
}
}
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Null => Nullable::Null,
Nullable::Value(v) => Nullable::Value(f(v)),
}
}
#[inline]
pub fn value(self) -> Option<T> {
match self {
Nullable::Value(v) => Some(v),
Nullable::Null => None,
}
}
#[inline]
pub fn into_option(self) -> Option<T> {
self.value()
}
#[inline]
#[track_caller]
pub fn unwrap(self) -> T {
match self {
Nullable::Value(v) => v,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Null` value"),
}
}
#[inline]
#[track_caller]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Value(v) => v,
Nullable::Null => panic!("{}", msg),
}
}
#[inline]
pub fn unwrap_or(self, default: T) -> T {
self.value().unwrap_or(default)
}
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
self.value().unwrap_or_else(f)
}
#[inline]
pub fn unwrap_or_default(self) -> T
where
T: Default,
{
self.value().unwrap_or_default()
}
#[inline]
pub fn is_value_and(self, f: impl FnOnce(T) -> bool) -> bool {
match self {
Nullable::Value(v) => f(v),
Nullable::Null => false,
}
}
#[inline]
pub fn is_null_or(self, f: impl FnOnce(T) -> bool) -> bool {
match self {
Nullable::Value(v) => f(v),
Nullable::Null => true,
}
}
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Value(v) => Ok(v),
Nullable::Null => Err(err),
}
}
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Value(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Value(v) => f(v),
Nullable::Null => Nullable::Null,
}
}
#[inline]
pub fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Nullable<T> {
match self {
Nullable::Value(v) if predicate(&v) => Nullable::Value(v),
_ => Nullable::Null,
}
}
#[inline]
pub fn take(&mut self) -> Nullable<T> {
core::mem::replace(self, Nullable::Null)
}
#[inline]
pub fn replace(&mut self, value: T) -> Nullable<T> {
core::mem::replace(self, Nullable::Value(value))
}
#[inline]
pub fn iter(&self) -> core::option::IntoIter<&T> {
self.as_ref().value().into_iter()
}
#[inline]
pub fn iter_mut(&mut self) -> core::option::IntoIter<&mut T> {
self.as_mut().value().into_iter()
}
}
impl<T: Deref> Nullable<T> {
#[inline]
pub fn as_deref(&self) -> Nullable<&T::Target> {
self.as_ref().map(|v| v.deref())
}
}
impl<T: DerefMut> Nullable<T> {
#[inline]
pub fn as_deref_mut(&mut self) -> Nullable<&mut T::Target> {
self.as_mut().map(|v| v.deref_mut())
}
}
impl<T: Copy> Nullable<&T> {
#[inline]
pub fn copied(self) -> Nullable<T> {
self.map(|&v| v)
}
}
impl<T: Clone> Nullable<&T> {
#[inline]
pub fn cloned(self) -> Nullable<T> {
self.map(|v| v.clone())
}
}
impl<T: Copy> Nullable<&mut T> {
#[inline]
pub fn copied(self) -> Nullable<T> {
self.map(|&mut v| v)
}
}
impl<T: Clone> Nullable<&mut T> {
#[inline]
pub fn cloned(self) -> Nullable<T> {
self.map(|v| v.clone())
}
}
impl<T> IntoIterator for Nullable<T> {
type Item = T;
type IntoIter = core::option::IntoIter<T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.value().into_iter()
}
}
impl<'a, T> IntoIterator for &'a Nullable<T> {
type Item = &'a T;
type IntoIter = core::option::IntoIter<&'a T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T> IntoIterator for &'a mut Nullable<T> {
type Item = &'a mut T;
type IntoIter = core::option::IntoIter<&'a mut T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[cfg(wasm_bindgen_unstable_try_trait)]
mod try_trait {
use core::convert::Infallible;
use core::ops::{ControlFlow, FromResidual, Residual, Try};
use super::Nullable;
impl<T> Residual<T> for Nullable<Infallible> {
type TryType = Nullable<T>;
}
impl<T> Try for Nullable<T> {
type Output = T;
type Residual = Nullable<Infallible>;
#[inline]
fn from_output(output: T) -> Self {
Nullable::Value(output)
}
#[inline]
fn branch(self) -> ControlFlow<Self::Residual, T> {
match self {
Nullable::Value(v) => ControlFlow::Continue(v),
Nullable::Null => ControlFlow::Break(Nullable::Null),
}
}
}
impl<T> FromResidual<Nullable<Infallible>> for Nullable<T> {
#[inline]
fn from_residual(_: Nullable<Infallible>) -> Self {
Nullable::Null
}
}
impl<T> FromResidual<Option<Infallible>> for Nullable<T> {
#[inline]
fn from_residual(_: Option<Infallible>) -> Self {
Nullable::Null
}
}
impl<T> FromResidual<Nullable<Infallible>> for Option<T> {
#[inline]
fn from_residual(_: Nullable<Infallible>) -> Self {
None
}
}
}
impl<T> From<T> for Nullable<T> {
#[inline]
fn from(value: T) -> Self {
Nullable::Value(value)
}
}
impl<T> From<Option<T>> for Nullable<T> {
#[inline]
fn from(option: Option<T>) -> Self {
match option {
Some(v) => Nullable::Value(v),
None => Nullable::Null,
}
}
}
impl<T> From<Nullable<T>> for Option<T> {
#[inline]
fn from(nullable: Nullable<T>) -> Self {
nullable.value()
}
}
impl<T: Into<JsValue>> From<Nullable<T>> for JsValue {
#[inline]
fn from(nullable: Nullable<T>) -> Self {
match nullable {
Nullable::Null => JsValue::NULL,
Nullable::Value(v) => v.into(),
}
}
}
impl<T: JsGeneric> From<Nullable<T>> for JsNullable<T> {
#[inline]
fn from(nullable: Nullable<T>) -> Self {
match nullable {
Nullable::Null => JsNullable::new(),
Nullable::Value(v) => JsNullable::wrap(v),
}
}
}
impl<T: JsGeneric> From<Option<Nullable<T>>> for JsNullable<T> {
#[inline]
fn from(option: Option<Nullable<T>>) -> Self {
match option {
Some(nullable) => nullable.into(),
None => JsValue::UNDEFINED.unchecked_into(),
}
}
}
impl<T: JsGeneric> From<JsNullable<T>> for Option<Nullable<T>> {
#[inline]
fn from(nullable: JsNullable<T>) -> Self {
if JsValue::is_undefined(&nullable) {
None
} else if JsValue::is_null(&nullable) {
Some(Nullable::Null)
} else {
Some(Nullable::Value(nullable.unchecked_into()))
}
}
}