use crate::value::{TryFromJs, TryIntoJs};
use boa_engine::{Context, JsResult, JsValue};
#[cfg(test)]
mod tests;
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash, Ord, Eq)]
pub enum Nullable<T> {
Null,
NonNull(T),
}
impl<T> Nullable<T> {
pub const fn is_null(&self) -> bool {
matches!(self, Nullable::Null)
}
pub const fn is_not_null(&self) -> bool {
matches!(self, Nullable::NonNull(_))
}
#[inline]
pub fn iter(&self) -> Iter<'_, T> {
self.into_iter()
}
#[inline]
pub const fn as_ref(&self) -> Nullable<&T> {
match self {
Nullable::Null => Nullable::Null,
Nullable::NonNull(t) => Nullable::NonNull(t),
}
}
#[inline]
pub fn map<U, F>(self, f: F) -> Nullable<U>
where
F: FnOnce(T) -> U,
{
match self {
Nullable::NonNull(x) => Nullable::NonNull(f(x)),
Nullable::Null => Nullable::Null,
}
}
#[inline]
pub fn unwrap_or_default(self) -> T
where
T: Default,
{
match self {
Nullable::NonNull(x) => x,
Nullable::Null => T::default(),
}
}
}
impl<'a, T> IntoIterator for &'a Nullable<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
#[inline]
fn into_iter(self) -> Iter<'a, T> {
Iter(match self {
Nullable::Null => None,
Nullable::NonNull(v) => Some(v),
})
}
}
impl<T> IntoIterator for Nullable<T> {
type Item = T;
type IntoIter = IntoIter<T>;
#[inline]
fn into_iter(self) -> IntoIter<T> {
IntoIter(self.into())
}
}
impl<T: TryFromJs> TryFromJs for Nullable<T> {
fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {
if value.is_null() {
Ok(Nullable::Null)
} else {
T::try_from_js(value, context).map(Nullable::NonNull)
}
}
}
impl<T: TryIntoJs> TryIntoJs for Nullable<T> {
fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {
match self {
Nullable::Null => Ok(JsValue::null()),
Nullable::NonNull(t) => t.try_into_js(context),
}
}
}
impl<T> From<Nullable<T>> for Option<T> {
#[inline]
fn from(value: Nullable<T>) -> Self {
match value {
Nullable::Null => None,
Nullable::NonNull(t) => Some(t),
}
}
}
impl<T> From<Option<T>> for Nullable<T> {
#[inline]
fn from(value: Option<T>) -> Self {
match value {
None => Nullable::Null,
Some(t) => Nullable::NonNull(t),
}
}
}
#[derive(Debug)]
pub struct Iter<'a, A>(Option<&'a A>);
impl<'a, T: 'a> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.0.take()
}
}
impl<'a, T: 'a> DoubleEndedIterator for Iter<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.take()
}
}
impl<A> ExactSizeIterator for Iter<'_, A> {
#[inline]
fn len(&self) -> usize {
usize::from(self.0.is_some())
}
}
#[derive(Debug)]
pub struct IntoIter<A>(Option<A>);
impl<A> Iterator for IntoIter<A> {
type Item = A;
fn next(&mut self) -> Option<Self::Item> {
self.0.take()
}
}
impl<A> DoubleEndedIterator for IntoIter<A> {
fn next_back(&mut self) -> Option<A> {
self.0.take()
}
}
impl<A> ExactSizeIterator for IntoIter<A> {
#[inline]
fn len(&self) -> usize {
usize::from(self.0.is_some())
}
}
impl<T> Nullable<Option<T>> {
#[inline]
pub fn flatten(self) -> Option<T> {
match self {
Nullable::Null | Nullable::NonNull(None) => None,
Nullable::NonNull(Some(t)) => Some(t),
}
}
}