use crate::{Pipe, Result};
use std::marker::PhantomData;
use tuplify::Unpack;
pub trait OrExt<I, O, E, R> {
fn or<P>(self, p: P) -> Or<Self, P>
where
Self: Sized,
I: Clone,
P: Pipe<I, O, E, R>,
{
Or::new(self, p)
}
fn or_self<O2, P>(self, p: P) -> OrSelf<O, O2, Self, P>
where
Self: Sized,
O: Unpack,
I: Clone,
P: Pipe<I, O2, E, R>,
{
OrSelf::new(self, p)
}
fn or_other<O2, P>(self, p: P) -> OrOther<O, O2, Self, P>
where
Self: Sized,
I: Clone,
O2: Unpack,
P: Pipe<I, O2, E, R>,
{
OrOther::new(self, p)
}
}
impl<I, O, E, R, P> OrExt<I, O, E, R> for P where P: Pipe<I, O, E, R> {}
pub struct Or<P, P1> {
p: P,
p1: P1,
}
impl<P, P1> Or<P, P1> {
fn new(p: P, p1: P1) -> Self { Self { p, p1 } }
}
impl<I, O, E, R, P, P1> Pipe<I, O, E, R> for Or<P, P1>
where
P: Pipe<I, O, E, R>,
P1: Pipe<I, O, E, R>,
I: Clone,
{
fn apply(&mut self, input: I) -> Result<R, O, E> {
match self.p.apply(input.clone()) {
x @ Ok(_) => x,
Err(x) => {
x.fatality()?;
self.p1.apply(input)
}
}
}
}
pub struct OrSelf<O, O2, P, P1> {
p: P,
p1: P1,
o: PhantomData<O>,
o2: PhantomData<O2>,
}
impl<O, O2, P, P1> OrSelf<O, O2, P, P1> {
fn new(p: P, p1: P1) -> Self { Self { p, p1, o: PhantomData, o2: PhantomData } }
}
impl<I, O, O2, E, R, P, P1> Pipe<I, (Option<O::Output>,), E, R> for OrSelf<O, O2, P, P1>
where
O: Unpack,
P: Pipe<I, O, E, R>,
P1: Pipe<I, O2, E, R>,
I: Clone,
{
fn apply(&mut self, input: I) -> Result<R, (Option<O::Output>,), E> {
match self.p.apply(input.clone()).map(|(x, y)| (x, (Some(y.unpack()),))) {
x @ Ok(_) => x,
Err(x) => {
x.fatality()?;
self.p1.apply(input).map(|(x, _)| (x, (None,)))
}
}
}
}
pub struct OrOther<O, O2, P, P1> {
p: P,
p1: P1,
o: PhantomData<O>,
o2: PhantomData<O2>,
}
impl<O, O2, P, P1> OrOther<O, O2, P, P1> {
fn new(p: P, p1: P1) -> Self { Self { p, p1, o: PhantomData, o2: PhantomData } }
}
impl<I, O, O2, E, R, P, P1> Pipe<I, (Option<O2::Output>,), E, R> for OrOther<O, O2, P, P1>
where
O2: Unpack,
P: Pipe<I, O, E, R>,
P1: Pipe<I, O2, E, R>,
I: Clone,
{
fn apply(&mut self, input: I) -> Result<R, (Option<O2::Output>,), E> {
match self.p.apply(input.clone()).map(|(x, _)| (x, (None,))) {
x @ Ok(_) => x,
Err(x) => {
x.fatality()?;
self.p1.apply(input).map(|(x, y)| (x, (Some(y.unpack()),)))
}
}
}
}
pub trait AnyOf<I, O, E, R> {
fn apply_any_of(&mut self, input: I) -> Result<R, O, E>;
}
pub fn any_of<I, O, E, R>(mut p: impl AnyOf<I, O, E, R>) -> impl Pipe<I, O, E, R> {
move |x| p.apply_any_of(x)
}
macro_rules! any_of_impl {
($_head:ident) => {};
($head:ident $($tail:ident) *) => {
any_of_impl!($($tail) *);
impl<I: Clone, O, E, R, $head: Pipe<I, O, E, R>, $($tail: Pipe<I, O, E, R>), *> AnyOf<I, O, E, R> for ($head, $($tail), *) {
#[allow(non_snake_case)]
fn apply_any_of(&mut self, input: I) -> Result<R, O, E> {
let ($head, $($tail), *) = self;
let e = $head.apply(input.clone());
if e.as_ref().map_or_else(|x|x.is_fatal(), |_| true) { return e; }
$(
let e = $tail.apply(input.clone());
if e.as_ref().map_or_else(|x|x.is_fatal(), |_| true) { return e; }
) *
e
}
}
};
}
any_of_impl!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19 T20 T21 T22 T23 T24 T25 T26 T27 T28 T29 T30 T31 T32);