pipei/
lib.rs

1//! # pipei
2//!
3//! `pipei` provides *pipe-style partial application* combinators.
4//!
5//! For each arity `i ≥ 0`, enabling feature `"i"` exports:
6//! - `Pipe{i}Ref` — borrow-based pipes
7//! - `Pipe{i}`    — by-value pipes
8//! - `Tap{i}Ref`  — borrow-based taps (discard return, return `self` for chaining)
9//! - `Tap{i}`     — by-value taps (discard return, return `self` for chaining)
10//!
11//! Laws (schematic):
12//!
13//! ```text
14//! self.pipe{i}(f)(a1..ai)                = f(self,            a1..ai)
15//! self.pipe{i}_with(proj, f)(a1..ai)     = f(proj(&self),     a1..ai)
16//! self.pipe{i}_with_mut(proj, f)(a1..ai) = f(proj(&mut self), a1..ai)
17//!
18//! self.tap{i}(f)(a1..ai)                 = { let _ = f(&mut self, a1..ai); self }
19//! self.tap{i}_with(proj, f)(a1..ai)      = { let _ = f(proj(&self), a1..ai); &self }
20//! self.tap{i}_with_mut(proj, f)(a1..ai)  = { let _ = f(proj(&mut self), a1..ai); &mut self }
21//! ```
22//!
23//! Only the arities whose numeric feature is enabled are compiled.
24
25// #![no_std]
26
27// --- Helper Types for tap{i}---
28pub struct Imm;
29pub struct Mut;
30
31#[cfg(feature = "0")]
32mod pipe_0 {
33    //! Arity-0 pipe combinators.
34
35    /// Borrow-based arity-0 piping.
36    ///
37    /// Fixes the first argument to `self` (possibly projected) and returns a
38    /// nullary closure.
39
40    use crate::{Imm, Mut};
41    pub trait Pipe0Ref {
42        /// Partially applies `f` by fixing its first argument to `proj(&self)`,
43        /// returning a nullary closure.
44        ///
45        /// Law: `self.pipe0_with(proj, f)() = f(proj(&self))`.
46        #[inline]
47        fn pipe0_with<'a, X: ?Sized + 'a, R: 'a>(
48            &'a self,
49            proj: impl FnOnce(&'a Self) -> &'a X,
50            f: impl FnOnce(&'a X) -> R,
51        ) -> impl FnOnce() -> R {
52            move || f(proj(self))
53        }
54
55        /// Partially applies `f` by fixing its first argument to `proj(&mut self)`,
56        /// returning a nullary closure.
57        ///
58        /// Law: `self.pipe0_with_mut(proj, f)() = f(proj(&mut self))`.
59        #[inline]
60        fn pipe0_with_mut<'a, X: ?Sized + 'a, R: 'a>(
61            &'a mut self,
62            proj: impl FnOnce(&'a mut Self) -> &'a mut X,
63            f: impl FnOnce(&'a mut X) -> R,
64        ) -> impl FnOnce() -> R {
65            move || f(proj(self))
66        }
67    }
68    impl<T: ?Sized> Pipe0Ref for T {}
69
70    /// By-value arity-0 piping.
71    ///
72    /// Moves `self` into the returned nullary closure.
73    pub trait Pipe0: Sized + Pipe0Ref {
74        /// Partially applies `f` by moving `self` into the returned nullary closure.
75        ///
76        /// Law: `self.pipe0(f)() = f(self)`.
77        #[inline]
78        fn pipe0<R>(self, f: impl FnOnce(Self) -> R) -> impl FnOnce() -> R {
79            move || f(self)
80        }
81    }
82    impl<T: Sized> Pipe0 for T {}
83
84    // --- Arity 0 Helpers ---
85
86    /// Auxiliary trait to abstract over mutable vs. immutable closures for arity 0.
87    pub trait Tap0Fn<Rec: ?Sized, Marker> {
88        fn call(self, rec: &mut Rec);
89    }
90
91    // Impl for Immutable Fn(&T) -> coerces &mut T to &T
92    impl<F, Rec: ?Sized, R> Tap0Fn<Rec, Imm> for F
93    where
94        F: FnOnce(&Rec) -> R
95    {
96        #[inline]
97        fn call(self, rec: &mut Rec) { (self)(rec); }
98    }
99
100    // Impl for Mutable Fn(&mut T) -> passes &mut T directly
101    impl<F, Rec: ?Sized, R> Tap0Fn<Rec, Mut> for F
102    where
103        F: FnOnce(&mut Rec) -> R
104    {
105        #[inline]
106        fn call(self, rec: &mut Rec) { (self)(rec); }
107    }
108
109    // --- Arity 0 Traits ---
110
111    pub trait Tap0Ref {
112        /// Immutable view tap. Always takes `Fn(&T)`.
113        #[inline]
114        fn tap0_with<'a, X: ?Sized + 'a, R>(
115            &'a self,
116            proj: impl FnOnce(&'a Self) -> &'a X,
117            f: impl FnOnce(&'a X) -> R,
118        ) -> impl FnOnce() -> &'a Self {
119            move || {
120                let _ = f(proj(self));
121                self
122            }
123        }
124
125        /// Mutable view tap. Accepts BOTH `Fn(&mut T)` and `Fn(&T)`.
126        #[inline]
127        fn tap0_with_mut<'a, X: ?Sized + 'a, M, F>(
128            &'a mut self,
129            proj: impl FnOnce(&mut Self) -> &mut X,
130            f: F,
131        ) -> impl FnOnce() -> &'a mut Self
132        where
133            F: Tap0Fn<X, M> // <--- The Fix: Uses the helper trait
134        {
135            move || {
136                f.call(proj(&mut *self));
137                self
138            }
139        }
140    }
141    impl<T: ?Sized> Tap0Ref for T {}
142
143    pub trait Tap0: Sized + Tap0Ref {
144        /// By-value tap. Accepts BOTH `Fn(&mut T)` and `Fn(&T)`.
145        #[inline]
146        fn tap0<M, F>(self, f: F) -> impl FnOnce() -> Self
147        where
148            F: Tap0Fn<Self, M>
149        {
150            move || {
151                let mut s = self;
152                f.call(&mut s);
153                s
154            }
155        }
156    }
157    impl<T: Sized> Tap0 for T {}
158}
159
160#[cfg(feature = "0")]
161pub use pipe_0::{Pipe0, Pipe0Ref, Tap0, Tap0Ref};
162
163use paste::paste;
164
165/// Generates `Pipe{i}Ref` + `Pipe{i}` and `Tap{i}Ref` + `Tap{i}` modules gated by numeric features.
166macro_rules! gen_pipei {
167  ($(($i:literal, $feat:literal)),+ $(,)?) => {
168    $(
169      paste! {
170        #[cfg(feature = $feat)]
171        #[doc = concat!("Arity-", $feat, " pipe/tap combinators.")]
172        mod [<pipe_ $i>] {
173          use pipei_macros::{pipei_traits, tapi_traits};
174          use crate::{Imm, Mut};
175          pipei_traits!($i);
176          tapi_traits!($i);
177        }
178
179        #[cfg(feature = $feat)]
180        pub use [<pipe_ $i>]::{
181          [<Pipe $i>], [<Pipe $i Ref>],
182          [<Tap $i>],  [<Tap $i Ref>],
183        };
184      }
185    )+
186  }
187}
188
189// Generate arities 1..=100 (each gated by feature "i").
190gen_pipei! {
191  (1,"1"), (2,"2"), (3,"3"), (4,"4"), (5,"5"),
192  (6,"6"), (7,"7"), (8,"8"), (9,"9"), (10,"10"),
193  (11,"11"), (12,"12"), (13,"13"), (14,"14"), (15,"15"),
194  (16,"16"), (17,"17"), (18,"18"), (19,"19"), (20,"20"),
195  (21,"21"), (22,"22"), (23,"23"), (24,"24"), (25,"25"),
196  (26,"26"), (27,"27"), (28,"28"), (29,"29"), (30,"30"),
197  (31,"31"), (32,"32"), (33,"33"), (34,"34"), (35,"35"),
198  (36,"36"), (37,"37"), (38,"38"), (39,"39"), (40,"40"),
199  (41,"41"), (42,"42"), (43,"43"), (44,"44"), (45,"45"),
200  (46,"46"), (47,"47"), (48,"48"), (49,"49"), (50,"50"),
201  (51,"51"), (52,"52"), (53,"53"), (54,"54"), (55,"55"),
202  (56,"56"), (57,"57"), (58,"58"), (59,"59"), (60,"60"),
203  (61,"61"), (62,"62"), (63,"63"), (64,"64"), (65,"65"),
204  (66,"66"), (67,"67"), (68,"68"), (69,"69"), (70,"70"),
205  (71,"71"), (72,"72"), (73,"73"), (74,"74"), (75,"75"),
206  (76,"76"), (77,"77"), (78,"78"), (79,"79"), (80,"80"),
207  (81,"81"), (82,"82"), (83,"83"), (84,"84"), (85,"85"),
208  (86,"86"), (87,"87"), (88,"88"), (89,"89"), (90,"90"),
209  (91,"91"), (92,"92"), (93,"93"), (94,"94"), (95,"95"),
210  (96,"96"), (97,"97"), (98,"98"), (99,"99"), (100,"100"),
211}