#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "__unstable"), forbid(unstable_features))]
#![cfg_attr(feature = "try_trait", feature(try_trait_v2))]
#[cfg(feature = "try_trait")]
mod try_trait;
use self::NullableResult::*;
#[cfg(doc)]
use core::convert::TryInto;
use core::{
convert::TryFrom,
fmt::Debug,
iter::{FilterMap, FromIterator, FusedIterator},
ops::Deref,
};
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[must_use]
pub enum NullableResult<T, E> {
Ok(T),
Err(E),
Null,
}
impl<T, E> Default for NullableResult<T, E> {
fn default() -> Self {
Null
}
}
impl<T, E: Debug> NullableResult<T, E> {
#[inline]
pub fn unwrap(self) -> T {
match self {
NullableResult::Ok(item) => item,
NullableResult::Err(err) => panic!(
"tried to unwrap a nullable result containing Err: {:?}",
err
),
NullableResult::Null => {
panic!("tried to unwrap a nullable result containing `Null`")
}
}
}
}
impl<T: Default, E> NullableResult<T, E> {
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Ok(item) => item,
_ => T::default(),
}
}
}
impl<T: Copy, E> NullableResult<&'_ T, E> {
#[inline]
pub fn copied(self) -> NullableResult<T, E> {
self.map(|&item| item)
}
}
impl<T: Copy, E> NullableResult<&'_ mut T, E> {
#[inline]
pub fn copied(self) -> NullableResult<T, E> {
self.map(|&mut item| item)
}
}
impl<T: Clone, E> NullableResult<&'_ T, E> {
#[inline]
pub fn cloned(self) -> NullableResult<T, E> {
self.map(|item| item.clone())
}
}
impl<T: Clone, E> NullableResult<&'_ mut T, E> {
#[inline]
pub fn cloned(self) -> NullableResult<T, E> {
self.map(|item| item.clone())
}
}
impl<T: Deref, E> NullableResult<T, E> {
#[inline]
pub fn as_deref(&self) -> NullableResult<&T::Target, &E> {
match self {
Ok(item) => Ok(item.deref()),
Err(err) => Err(err),
Null => Null,
}
}
}
impl<T, E> NullableResult<T, E> {
#[inline]
#[must_use]
pub fn is_ok(&self) -> bool {
matches!(self, Ok(_))
}
#[inline]
#[must_use]
pub fn is_err(&self) -> bool {
matches!(self, Err(_))
}
#[inline]
#[must_use]
pub fn is_null(&self) -> bool {
matches!(self, Null)
}
#[inline]
#[track_caller]
pub fn expect(self, msg: &str) -> T {
match self {
Ok(item) => item,
_ => panic!("{}", msg),
}
}
#[inline]
pub fn unwrap_or(self, item: T) -> T {
match self {
Ok(item) => item,
_ => item,
}
}
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Ok(item) => item,
_ => f(),
}
}
#[inline]
pub fn option(self) -> Option<T> {
match self {
Ok(item) => Some(item),
Err(_) | Null => None,
}
}
#[inline]
pub fn optional_result(self) -> Option<Result<T, E>> {
self.into()
}
#[inline]
pub fn resulting_option(self) -> Result<Option<T>, E> {
self.into()
}
#[inline]
pub fn result(self, err: E) -> Result<T, E> {
match self {
Ok(item) => Result::Ok(item),
Err(err) => Result::Err(err),
Null => Result::Err(err),
}
}
#[inline]
pub fn result_with<F: FnOnce() -> E>(self, f: F) -> Result<T, E> {
match self {
Ok(item) => Result::Ok(item),
Err(err) => Result::Err(err),
Null => Result::Err(f()),
}
}
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> NullableResult<U, E> {
match self {
Ok(item) => Ok(f(item)),
Err(err) => Err(err),
Null => Null,
}
}
#[inline]
pub fn map_err<U, F: FnOnce(E) -> U>(self, f: F) -> NullableResult<T, U> {
match self {
Ok(item) => NullableResult::Ok(item),
Err(err) => NullableResult::Err(f(err)),
Null => NullableResult::Null,
}
}
#[inline]
pub fn result_optional_err(self) -> Result<T, Option<E>> {
match self {
Ok(item) => Result::Ok(item),
Err(err) => Result::Err(Some(err)),
Null => Result::Err(None),
}
}
#[inline]
pub fn as_ref(&self) -> NullableResult<&T, &E> {
use NullableResult::*;
match self {
Ok(item) => Ok(item),
Err(err) => Err(err),
Null => Null,
}
}
#[inline]
pub fn as_mut(&mut self) -> NullableResult<&mut T, &mut E> {
use NullableResult::*;
match self {
Ok(item) => Ok(item),
Err(err) => Err(err),
Null => Null,
}
}
#[inline]
pub fn and<U>(self, res: NullableResult<U, E>) -> NullableResult<U, E> {
match self {
Ok(_) => res,
Err(err) => Err(err),
Null => Null,
}
}
#[inline]
pub fn and_then<U, F>(self, op: F) -> NullableResult<U, E>
where
F: FnOnce(T) -> NullableResult<U, E>,
{
match self {
Ok(item) => op(item),
Err(err) => Err(err),
Null => Null,
}
}
}
impl<T, E> NullableResult<NullableResult<T, E>, E> {
#[inline]
pub fn flatten(self) -> NullableResult<T, E> {
match self {
Ok(Ok(item)) => Ok(item),
Ok(Err(err)) | Err(err) => Err(err),
Ok(Null) | Null => Null,
}
}
}
impl<T, E> From<Result<Option<T>, E>> for NullableResult<T, E> {
#[inline]
fn from(res: Result<Option<T>, E>) -> Self {
match res {
Result::Ok(Option::Some(item)) => Ok(item),
Result::Ok(None) => Null,
Result::Err(err) => Err(err),
}
}
}
impl<T, E> From<NullableResult<T, E>> for Result<Option<T>, E> {
#[inline]
fn from(nr: NullableResult<T, E>) -> Self {
match nr {
Ok(item) => Result::Ok(Some(item)),
Err(err) => Result::Err(err),
Null => Result::Ok(None),
}
}
}
impl<T, E> From<Result<T, E>> for NullableResult<T, E> {
#[inline]
fn from(res: Result<T, E>) -> Self {
match res {
Result::Ok(item) => Ok(item),
Result::Err(err) => Err(err),
}
}
}
impl<T, E> From<Option<Result<T, E>>> for NullableResult<T, E> {
#[inline]
fn from(opt: Option<Result<T, E>>) -> Self {
match opt {
None => Null,
Some(Result::Ok(item)) => Ok(item),
Some(Result::Err(err)) => Err(err),
}
}
}
impl<T, E> From<NullableResult<T, E>> for Option<Result<T, E>> {
#[inline]
fn from(nr: NullableResult<T, E>) -> Self {
match nr {
Ok(item) => Some(Result::Ok(item)),
Err(err) => Some(Result::Err(err)),
Null => None,
}
}
}
impl<T, E> From<Option<T>> for NullableResult<T, E> {
#[inline]
fn from(opt: Option<T>) -> Self {
match opt {
Some(item) => Ok(item),
None => Null,
}
}
}
impl<T, E> From<Result<T, Option<E>>> for NullableResult<T, E> {
#[inline]
fn from(res: Result<T, Option<E>>) -> Self {
match res {
Result::Ok(item) => Ok(item),
Result::Err(Some(err)) => Err(err),
Result::Err(None) => Null,
}
}
}
impl<T, E, C> FromIterator<NullableResult<T, E>> for NullableResult<C, E>
where
C: FromIterator<T>,
{
fn from_iter<I: IntoIterator<Item = NullableResult<T, E>>>(
iter: I,
) -> Self {
let result = iter
.into_iter()
.map(NullableResult::result_optional_err)
.collect::<Result<_, _>>();
NullableResult::from(result)
}
}
#[macro_export]
macro_rules! extract {
($nr:expr) => {
extract!($nr, _)
};
($nr:expr, $err:ty) => {{
let nr = $crate::NullableResult::<_, $err>::from($nr);
match nr {
$crate::NullableResult::Ok(item) => item,
$crate::NullableResult::Err(err) => {
return $crate::NullableResult::Err(err.into());
}
$crate::NullableResult::Null => {
return $crate::NullableResult::Null;
}
}
}};
}
pub trait GeneralIterExt: Iterator {
fn try_find<E, P>(self, pred: P) -> NullableResult<Self::Item, E>
where
P: FnMut(&Self::Item) -> Result<bool, E>;
fn try_find_map<T, E, F>(self, f: F) -> NullableResult<T, E>
where
F: FnMut(Self::Item) -> NullableResult<T, E>;
fn maybe_try_fold<T, E, Op>(
&mut self,
init: T,
op: Op,
) -> NullableResult<T, E>
where
Op: FnMut(T, Self::Item) -> NullableResult<T, E>;
}
impl<I: Iterator> GeneralIterExt for I {
#[inline]
fn try_find<E, P>(self, mut pred: P) -> NullableResult<Self::Item, E>
where
P: FnMut(&Self::Item) -> Result<bool, E>,
{
for item in self {
return match pred(&item) {
Result::Err(err) => Err(err),
Result::Ok(true) => Ok(item),
Result::Ok(false) => continue,
};
}
Null
}
#[inline]
fn try_find_map<T, E, F>(self, mut f: F) -> NullableResult<T, E>
where
F: FnMut(Self::Item) -> NullableResult<T, E>,
{
for item in self {
return match f(item) {
Ok(item) => Ok(item),
Err(err) => Err(err),
Null => continue,
};
}
Null
}
#[inline]
fn maybe_try_fold<T, E, Op>(
&mut self,
init: T,
mut op: Op,
) -> NullableResult<T, E>
where
Op: FnMut(T, Self::Item) -> NullableResult<T, E>,
{
self.try_fold(init, |prev, curr| op(prev, curr).result_optional_err())
.into()
}
}
pub trait IterExt<T, E>: Iterator<Item = NullableResult<T, E>>
where
Self: Sized,
{
#[inline]
fn filter_nulls(self) -> FilterNulls<Self, T, E> {
self.filter_map(Option::from)
}
#[inline]
fn extract_and_find<P>(self, mut pred: P) -> NullableResult<T, E>
where
P: FnMut(&T) -> Result<bool, E>,
{
self.try_find_map(|item| {
let item = extract!(item);
match pred(&item) {
Result::Err(err) => Err(err),
Result::Ok(true) => Ok(item),
Result::Ok(false) => Null,
}
})
}
#[inline]
fn extract_and_find_map<F, U>(self, mut f: F) -> NullableResult<U, E>
where
F: FnMut(T) -> NullableResult<U, E>,
{
self.try_find_map(|item| f(extract!(item)))
}
#[inline]
fn try_filter<P>(self, pred: P) -> TryFilter<Self, P, T, E>
where
P: FnMut(&T) -> bool,
{
TryFilter { inner: self, pred }
}
fn try_filter_map<F, U>(self, f: F) -> TryFilterMap<Self, F, T, U, E>
where
F: FnMut(T) -> Option<NullableResult<U, E>>,
{
TryFilterMap { inner: self, f }
}
}
impl<I, T, E> IterExt<T, E> for I where I: Iterator<Item = NullableResult<T, E>> {}
type FilterNulls<I, T, E> =
FilterMap<I, fn(NullableResult<T, E>) -> Option<Result<T, E>>>;
pub struct TryFilter<I, P, T, E>
where
I: Iterator<Item = NullableResult<T, E>>,
P: FnMut(&T) -> bool,
{
inner: I,
pred: P,
}
impl<I, P, T, E> Iterator for TryFilter<I, P, T, E>
where
I: Iterator<Item = NullableResult<T, E>>,
P: FnMut(&T) -> bool,
{
type Item = NullableResult<T, E>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
None => None,
Some(Null) => Some(Null),
Some(Err(err)) => Some(Err(err)),
Some(Ok(item)) if (self.pred)(&item) => Some(Ok(item)),
Some(Ok(_)) => self.next(),
}
}
}
impl<I, P, T, E> FusedIterator for TryFilter<I, P, T, E>
where
I: FusedIterator<Item = NullableResult<T, E>>,
P: FnMut(&T) -> bool,
{
}
pub struct TryFilterMap<I, F, T, U, E>
where
I: Iterator<Item = NullableResult<T, E>>,
F: FnMut(T) -> Option<NullableResult<U, E>>,
{
inner: I,
f: F,
}
impl<I, F, T, U, E> Iterator for TryFilterMap<I, F, T, U, E>
where
I: Iterator<Item = NullableResult<T, E>>,
F: FnMut(T) -> Option<NullableResult<U, E>>,
{
type Item = NullableResult<U, E>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
None => None,
Some(Null) => Some(Null),
Some(Err(err)) => Some(Err(err)),
Some(Ok(item)) => (self.f)(item),
}
}
}
impl<I, F, T, U, E> FusedIterator for TryFilterMap<I, F, T, U, E>
where
I: FusedIterator<Item = NullableResult<T, E>>,
F: FnMut(T) -> Option<NullableResult<U, E>>,
{
}
pub trait MaybeTryFrom<T>: Sized {
type Error;
fn maybe_try_from(item: T) -> NullableResult<Self, Self::Error>;
}
pub trait MaybeTryInto<T>: Sized {
type Error;
fn maybe_try_into(self) -> NullableResult<T, Self::Error>;
}
impl<T, U: TryFrom<T>> MaybeTryFrom<T> for U {
type Error = U::Error;
#[inline]
fn maybe_try_from(item: T) -> NullableResult<Self, Self::Error> {
U::try_from(item).into()
}
}
impl<T, U: MaybeTryFrom<T>> MaybeTryInto<U> for T {
type Error = U::Error;
#[inline]
fn maybe_try_into(self) -> NullableResult<U, Self::Error> {
U::maybe_try_from(self)
}
}