pipette 0.1.0

Polymorphic function pipelines without traits or macros
Documentation
#![doc = include_str!("../README.md")]

///
///
/// # Example
/// ```rust
/// let input = 1;
///
/// let output = pipette::pipe((
///     input
///     |a| a * 2,
///     |a| a * 3,
///     |a| a * 4,
///     |a| a * 5,
///     |a| a * 6,
///     |a| a * 7,
///     |a| a * 8,
/// ));
///
/// assert_eq!(output, 40_320);
/// ```
pub fn pipe<O>(p: impl Pipeline<O>) -> O {
    p.compute()
}

pub trait Pipeline<O> {
    fn compute(self) -> O;
}

impl<I, O, AI, A, B> Pipeline<O> for (I, A, B)
where
    A: FnOnce(I) -> AI,
    B: FnOnce(AI) -> O,
{
    fn compute(self) -> O {
        let (input, a, b) = self;
        b(a(input))
    }
}

impl<I, O, AI, BI, A, B, C> Pipeline<O> for (I, A, B, C)
where
    A: FnOnce(I) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> O,
{
    fn compute(self) -> O {
        let (input, a, b, c) = self;
        c(b(a(input)))
    }
}

impl<I, O, AI, BI, CI, A, B, C, D> Pipeline<O> for (I, A, B, C, D)
where
    A: FnOnce(I) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> O,
{
    fn compute(self) -> O {
        let (input, a, b, c, d) = self;
        d(c(b(a(input))))
    }
}

impl<I, O, AI, BI, CI, DI, A, B, C, D, E> Pipeline<O> for (I, A, B, C, D, E)
where
    A: FnOnce(I) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> DI,
    E: FnOnce(DI) -> O,
{
    fn compute(self) -> O {
        let (input, a, b, c, d, e) = self;
        e(d(c(b(a(input)))))
    }
}

impl<I, O, AI, BI, CI, DI, EI, A, B, C, D, E, F> Pipeline<O> for (I, A, B, C, D, E, F)
where
    A: FnOnce(I) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> DI,
    E: FnOnce(DI) -> EI,
    F: FnOnce(EI) -> O,
{
    fn compute(self) -> O {
        let (input, a, b, c, d, e, f) = self;
        f(e(d(c(b(a(input))))))
    }
}

impl<I, O, AI, BI, CI, DI, EI, FI, A, B, C, D, E, F, G> Pipeline<O> for (I, A, B, C, D, E, F, G)
where
    A: FnOnce(I) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> DI,
    E: FnOnce(DI) -> EI,
    F: FnOnce(EI) -> FI,
    G: FnOnce(FI) -> O,
{
    fn compute(self) -> O {
        let (input, a, b, c, d, e, f, g) = self;
        g(f(e(d(c(b(a(input)))))))
    }
}

impl<I, O, AI, BI, CI, DI, EI, FI, GI, A, B, C, D, E, F, G, H> Pipeline<O>
    for (I, A, B, C, D, E, F, G, H)
where
    A: FnOnce(I) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> DI,
    E: FnOnce(DI) -> EI,
    F: FnOnce(EI) -> FI,
    G: FnOnce(FI) -> GI,
    H: FnOnce(GI) -> O,
{
    fn compute(self) -> O {
        let (input, a, b, c, d, e, f, g, h) = self;
        h(g(f(e(d(c(b(a(input))))))))
    }
}

impl<IN, OUT, AI, BI, CI, DI, EI, FI, GI, HI, A, B, C, D, E, F, G, H, I> Pipeline<OUT>
    for (IN, A, B, C, D, E, F, G, H, I)
where
    A: FnOnce(IN) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> DI,
    E: FnOnce(DI) -> EI,
    F: FnOnce(EI) -> FI,
    G: FnOnce(FI) -> GI,
    H: FnOnce(GI) -> HI,
    I: FnOnce(HI) -> OUT,
{
    fn compute(self) -> OUT {
        let (input, a, b, c, d, e, f, g, h, i) = self;
        i(h(g(f(e(d(c(b(a(input)))))))))
    }
}

impl<IN, OUT, AI, BI, CI, DI, EI, FI, GI, HI, II, A, B, C, D, E, F, G, H, I, J> Pipeline<OUT>
    for (IN, A, B, C, D, E, F, G, H, I, J)
where
    A: FnOnce(IN) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> DI,
    E: FnOnce(DI) -> EI,
    F: FnOnce(EI) -> FI,
    G: FnOnce(FI) -> GI,
    H: FnOnce(GI) -> HI,
    I: FnOnce(HI) -> II,
    J: FnOnce(II) -> OUT,
{
    fn compute(self) -> OUT {
        let (input, a, b, c, d, e, f, g, h, i, j) = self;
        j(i(h(g(f(e(d(c(b(a(input))))))))))
    }
}

impl<IN, OUT, AI, BI, CI, DI, EI, FI, GI, HI, II, JI, A, B, C, D, E, F, G, H, I, J, K> Pipeline<OUT>
    for (IN, A, B, C, D, E, F, G, H, I, J, K)
where
    A: FnOnce(IN) -> AI,
    B: FnOnce(AI) -> BI,
    C: FnOnce(BI) -> CI,
    D: FnOnce(CI) -> DI,
    E: FnOnce(DI) -> EI,
    F: FnOnce(EI) -> FI,
    G: FnOnce(FI) -> GI,
    H: FnOnce(GI) -> HI,
    I: FnOnce(HI) -> II,
    J: FnOnce(II) -> JI,
    K: FnOnce(JI) -> OUT,
{
    fn compute(self) -> OUT {
        let (input, a, b, c, d, e, f, g, h, i, j, k) = self;
        k(j(i(h(g(f(e(d(c(b(a(input)))))))))))
    }
}

#[test]
fn pipe_works() {
    fn add_one(a: i32) -> i32 {
        a + 1
    }

    let r0 = pipe((0, add_one, add_one));

    let r1 = pipe((0, add_one, add_one, add_one));

    let r2 = pipe((0, add_one, add_one, add_one, add_one));

    let r3 = pipe((0, add_one, add_one, add_one, add_one, add_one));

    let r4 = pipe((0, add_one, add_one, add_one, add_one, add_one, add_one));

    let r5 = pipe((
        0, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
    ));
    let r6 = pipe((
        0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
    ));

    let r7 = pipe((
        0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
    ));

    let r8 = pipe((
        0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
    ));

    let r9 = pipe((
        0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
        add_one, add_one,
    ));

    debug_assert_eq!(r0, 2);
    debug_assert_eq!(r1, 3);
    debug_assert_eq!(r2, 4);
    debug_assert_eq!(r3, 5);
    debug_assert_eq!(r4, 6);
    debug_assert_eq!(r5, 7);
    debug_assert_eq!(r6, 8);
    debug_assert_eq!(r7, 9);
    debug_assert_eq!(r8, 10);
    debug_assert_eq!(r9, 11);

    let r9 = pipe((
        //
        1_i32,
        |a| a * 2,
        |a| a * 2,
        |a| a * 2,
        |a| a * 2,
        |a| a * 2,
        |a| a * 2,
        |a| a * 2,
    ));

    debug_assert_eq!(r9, 1024);
}