#![doc = include_str!("../README.md")]
#![feature(
fn_traits,
macro_metavar_expr,
decl_macro,
unboxed_closures,
associated_type_defaults,
auto_traits,
negative_impls
)]
#![allow(internal_features, incomplete_features)]
pub mod prelude;
pub mod also;
pub mod on;
pub mod take_if;
pub mod void;
pub use paste::paste;
pub struct PartialPipeline<T>(T);
pub struct PartialIgnoredPipeline<T>(T);
#[inline]
pub const fn pipe<T>(inner: T) -> PartialPipeline<T> {
PartialPipeline(inner)
}
#[inline]
pub const fn ignore<T>(inner: T) -> PartialIgnoredPipeline<T> {
PartialIgnoredPipeline(inner)
}
impl<T> PartialPipeline<T> {
#[inline]
pub fn ignore(self) -> PartialIgnoredPipeline<T> {
PartialIgnoredPipeline(self.0)
}
}
pub trait Pipe<T> {
type Output;
fn complete(self, value: T) -> Self::Output;
}
impl<P, T, R> std::ops::Shr<P> for PartialPipeline<T>
where
P: Pipe<T, Output = R>,
{
type Output = R;
#[inline]
fn shr(self, pipe: P) -> Self::Output {
pipe.complete(self.0)
}
}
impl<P, T, R> std::ops::Shr<P> for PartialIgnoredPipeline<T>
where
P: Pipe<T, Output = R>,
{
type Output = ();
#[inline]
fn shr(self, pipe: P) -> Self::Output {
pipe.complete(self.0);
}
}
#[cfg(feature = "fn-pipes")]
pub(crate) auto trait MarkerFnPipe {}
#[cfg(feature = "fn-pipes")]
impl<F: FnOnce(T) -> R, T, R> Pipe<T> for F
where
F: MarkerFnPipe,
{
type Output = R;
#[inline]
fn complete(self, value: T) -> <Self as Pipe<T>>::Output {
self(value)
}
}
macro_rules! generate_pipe_ntup_impl {
(
$(($pF:ident -> $($pN:ident),+ -> $pL:ident)),* $(,)?
) => (::paste::paste! {
$(generate_pipe_ntup_impl!(@gen_item
[impl<T, $pF, $($pN),+, $pL, [<R $pF>], $([<R $pN>]),+, [<R $pL>]> $crate::Pipe<T> for ($pF, $($pN),+, $pL)]
[$pF: $crate::Pipe<T, Output = [<R $pF>]>,]
($pF, $($pN),+, $pL)
[{
type Output = [<R $pL>];
fn complete(self, value: T) -> Self::Output {
let ([<$pF:lower>], $([<$pN:lower>]),+, [<$pL:lower>]) = self;
let item = [<$pF:lower>].complete(value);
$(
let item = [<$pN:lower>].complete(item);
)+
[<$pL:lower>].complete(item)
}
}]
);)*
});
(@gen_item [$($pre_clause:tt)+] [$($buffer:tt)+] ($pL:ident, $pC:ident $(, $pN:ident)*) [$($post_clause:tt)+]) => (::paste::paste! {
generate_pipe_ntup_impl!(@gen_item
[$($pre_clause)+]
[$($buffer)+ $pC: $crate::Pipe<[<R $pL>], Output = [<R $pC>]>,]
($pC $(, $pN)*)
[$($post_clause)+]
);
});
(@gen_item [$($pre_clause:tt)+] [$($buffer:tt)+] ($($residual:tt)*) [$($post_clause:tt)+]) => {
$($pre_clause)+
where $($buffer)+
$($post_clause)+
};
}
impl<T, P1, P2, RP1, RP2> Pipe<T> for (P1, P2)
where
P1: Pipe<T, Output = RP1>,
P2: Pipe<RP1, Output = RP2>,
{
type Output = RP2;
#[inline]
fn complete(self, value: T) -> Self::Output {
self.1.complete(self.0.complete(value))
}
}
generate_pipe_ntup_impl!(
(P1 -> P2 -> P3),
(P1 -> P2, P3 -> P4),
(P1 -> P2, P3, P4 -> P5),
(P1 -> P2, P3, P4, P5 -> P6),
(P1 -> P2, P3, P4, P5, P6 -> P7),
(P1 -> P2, P3, P4, P5, P6, P7 -> P8),
(P1 -> P2, P3, P4, P5, P6, P7, P8 -> P9),
(P1 -> P2, P3, P4, P5, P6, P7, P8, P9 -> P10),
(P1 -> P2, P3, P4, P5, P6, P7, P8, P9, P10 -> P11),
(P1 -> P2, P3, P4, P5, P6, P7, P8, P9, P10, P11 -> P12),
(P1 -> P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12 -> P13),
(P1 -> P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13 -> P14),
(P1 -> P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14 -> P15),
(P1 -> P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15 -> P16),
);
#[cfg(test)]
mod tests {
use super::*;
const fn add_one(to: i32) -> i32 {
to + 1
}
#[test]
fn it_works() {
assert_eq!(2, pipe(0) >> (add_one, add_one));
}
#[test]
fn a_closure_works_as_a_pipe() {
assert_eq!(1, pipe(0) >> |x| x + 1);
}
#[test]
fn tuples_of_pipes_can_be_infinitely_nested() {
assert_eq!(
6,
pipe(0) >> (add_one, add_one, (add_one, add_one, (add_one, add_one)))
);
}
#[test]
fn custom_pipe_implementation() {
struct Subtractor<const N: i32>;
impl<const N: i32> Pipe<i32> for Subtractor<N> {
type Output = i32;
fn complete(self, value: i32) -> Self::Output {
value - N
}
}
assert_eq!(-2, pipe(0) >> Subtractor::<2>);
}
#[test]
fn desugared_method() {
struct Int32(i32);
impl Int32 {
fn add_one(self) -> Self {
Self(self.0 + 1)
}
}
assert_eq!(1i32, pipe(Int32(0)) >> (Int32::add_one, |Int32(n)| n));
}
#[test]
fn ignore_pipeline_result() {
assert_eq!((), pipe(0).ignore() >> |x| x + 1);
}
}