tiered-result 0.1.0

A different approach to error handling
Documentation
use crate::{ClientResponse, FatalOrOk, FatalResult, TieredResult};
use core::{
    convert::Infallible,
    ops::{ControlFlow, FromResidual, Try},
};
use nullable_result::NullableResult;

pub struct Residual<F>(F);

impl<T, E, F> Try for TieredResult<T, E, F> {
    type Output = NullableResult<T, E>;
    type Residual = Residual<F>;

    fn from_output(output: Self::Output) -> Self {
        match output {
            NullableResult::Ok(item) => TieredResult::Ok(item),
            NullableResult::Err(err) => TieredResult::Err(err),
            NullableResult::Null => TieredResult::Null,
        }
    }

    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        use ControlFlow::*;
        use NullableResult::*;

        match self {
            TieredResult::Ok(item) => Continue(Ok(item)),
            TieredResult::Err(err) => Continue(Err(err)),
            TieredResult::Null => Continue(Null),
            TieredResult::Fatal(fatality) => Break(Residual(fatality)),
        }
    }
}

impl<T, E, F> FromResidual<Residual<F>> for TieredResult<T, E, F> {
    fn from_residual(residual: Residual<F>) -> Self {
        TieredResult::Fatal(residual.0)
    }
}

impl<T, E1, E2, F> FromResidual<NullableResult<Infallible, E1>>
    for TieredResult<T, E2, F>
where
    E1: Into<E2>,
{
    fn from_residual(residual: NullableResult<Infallible, E1>) -> Self {
        match residual {
            NullableResult::Ok(_) => unreachable!(),
            NullableResult::Err(err) => TieredResult::Err(err.into()),
            NullableResult::Null => TieredResult::Null,
        }
    }
}

impl<T, E1, E2, F> FromResidual<Result<Infallible, E1>>
    for TieredResult<T, E2, F>
where
    E1: Into<E2>,
{
    fn from_residual(residual: Result<Infallible, E1>) -> Self {
        match residual {
            Ok(_) => unreachable!(),
            Err(err) => TieredResult::Err(err.into()),
        }
    }
}

impl<T, E, F> FromResidual<Option<Infallible>> for TieredResult<T, E, F> {
    fn from_residual(_: Option<Infallible>) -> Self {
        TieredResult::Null
    }
}

impl<T, F> Try for FatalResult<T, F> {
    type Output = Option<T>;
    type Residual = Residual<F>;

    fn from_output(output: Self::Output) -> Self {
        match output {
            Some(value) => FatalResult::Ok(value),
            None => FatalResult::Null,
        }
    }

    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        use ControlFlow::*;

        match self {
            FatalResult::Ok(value) => Continue(Some(value)),
            FatalResult::Null => Continue(None),
            FatalResult::Fatal(fatality) => Break(Residual(fatality)),
        }
    }
}

impl<T, F> FromResidual<Residual<F>> for FatalResult<T, F> {
    fn from_residual(residual: Residual<F>) -> Self {
        FatalResult::Fatal(residual.0)
    }
}

impl<T, F> FromResidual<Option<Infallible>> for FatalResult<T, F> {
    fn from_residual(_: Option<Infallible>) -> Self {
        FatalResult::Null
    }
}

impl<T, F> Try for FatalOrOk<T, F> {
    type Output = T;
    type Residual = Residual<F>;

    fn from_output(output: Self::Output) -> Self {
        FatalOrOk::Ok(output)
    }

    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        use ControlFlow::*;

        match self {
            FatalOrOk::Ok(value) => Continue(value),
            FatalOrOk::Fatal(fatality) => Break(Residual(fatality)),
        }
    }
}

impl<T, F> FromResidual<Residual<F>> for FatalOrOk<T, F> {
    fn from_residual(residual: Residual<F>) -> Self {
        FatalOrOk::Fatal(residual.0)
    }
}

impl<F, C> Try for ClientResponse<F, C> {
    type Output = C;
    type Residual = Residual<F>;

    fn from_output(output: Self::Output) -> Self {
        ClientResponse::Continue(output)
    }

    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        use ControlFlow::*;

        match self {
            ClientResponse::Throw(err) => Break(Residual(err)),
            ClientResponse::Continue(continuation) => Continue(continuation),
        }
    }
}

impl<F, C> FromResidual<Residual<F>> for ClientResponse<F, C> {
    fn from_residual(residual: Residual<F>) -> Self {
        ClientResponse::Throw(residual.0)
    }
}