use core::convert::Infallible;
use core::fmt::{self, Debug, Formatter};
use core::marker::PhantomData;
use crate::constraint::ExpectConstrained as _;
use crate::expression::{Defined, Expression, Undefined};
use crate::sealed::{Sealed, StaticDebug};
pub trait Continue: Sealed + StaticDebug {
type As<P, E>;
fn continue_with_output<P, E>(output: P) -> Self::As<P, E>;
}
pub trait Break: Continue {
fn break_with_error<P, E>(error: E) -> Self::As<P, E>;
}
pub trait NonResidual<P, E>: Continue<As<P, E> = P> {}
impl<P, E, K> NonResidual<P, E> for K where K: Continue<As<P, E> = P> {}
#[derive(Debug)]
pub enum AsExpression {}
impl Break for AsExpression {
fn break_with_error<P, E>(error: E) -> Self::As<P, E> {
Undefined(error)
}
}
impl Continue for AsExpression {
type As<P, E> = Expression<P, E>;
fn continue_with_output<P, E>(output: P) -> Self::As<P, E> {
Defined(output)
}
}
impl Sealed for AsExpression {}
impl StaticDebug for AsExpression {
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "AsExpression")
}
}
#[derive(Debug)]
pub enum AsOption {}
impl Break for AsOption {
fn break_with_error<P, E>(_: E) -> Self::As<P, E> {
None
}
}
impl Continue for AsOption {
type As<P, E> = Option<P>;
fn continue_with_output<P, E>(output: P) -> Self::As<P, E> {
Some(output)
}
}
impl Sealed for AsOption {}
impl StaticDebug for AsOption {
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "AsOption")
}
}
#[derive(Debug)]
pub enum AsResult {}
impl Break for AsResult {
fn break_with_error<P, E>(error: E) -> Self::As<P, E> {
Err(error)
}
}
impl Continue for AsResult {
type As<P, E> = Result<P, E>;
fn continue_with_output<P, E>(output: P) -> Self::As<P, E> {
Ok(output)
}
}
impl Sealed for AsResult {}
impl StaticDebug for AsResult {
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "AsResult")
}
}
#[derive(Debug)]
pub enum AsSelf {}
impl Continue for AsSelf {
type As<P, E> = P;
fn continue_with_output<P, E>(output: P) -> Self::As<P, E> {
output
}
}
impl Sealed for AsSelf {}
impl StaticDebug for AsSelf {
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "AsSelf")
}
}
pub trait Divergence: Sealed + StaticDebug {
type Continue: Continue;
fn diverge<T, E>(result: Result<T, E>) -> <Self::Continue as Continue>::As<T, E>
where
E: Debug;
}
pub type ContinueFor<D> = <D as Divergence>::Continue;
pub type OutputFor<D, P, E> = <ContinueFor<D> as Continue>::As<P, E>;
#[derive(Debug)]
pub struct OrPanic<K = AsSelf>(PhantomData<fn() -> K>, Infallible);
impl<K> Divergence for OrPanic<K>
where
K: Continue,
{
type Continue = K;
fn diverge<T, E>(result: Result<T, E>) -> K::As<T, E>
where
E: Debug,
{
K::continue_with_output(result.expect_constrained())
}
}
impl<K> Sealed for OrPanic<K> {}
impl<K> StaticDebug for OrPanic<K>
where
K: StaticDebug,
{
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "OrPanic<")?;
K::fmt(formatter)?;
write!(formatter, ">")
}
}
pub struct OrError<K = AsExpression>(PhantomData<fn() -> K>, Infallible);
impl<K> Divergence for OrError<K>
where
K: Break,
{
type Continue = K;
fn diverge<T, E>(result: Result<T, E>) -> K::As<T, E>
where
E: Debug,
{
match result {
Ok(output) => K::continue_with_output(output),
Err(error) => K::break_with_error(error),
}
}
}
impl<K> Sealed for OrError<K> {}
impl<K> StaticDebug for OrError<K>
where
K: StaticDebug,
{
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "OrError<")?;
K::fmt(formatter)?;
write!(formatter, ">")
}
}