├── .gitignore
├── .vscode
└── settings.json
├── Cargo.toml
├── README.md
└── src
└── lib.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rust-analyzer.inlayHints.enable": true
3 | }
4 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "pipette"
3 | version = "0.1.0"
4 | edition = "2018"
5 | authors = ["Jonathan Kelley"]
6 | license = "MIT"
7 | description = "Polymorphic function pipelines without traits or macros"
8 | tags = ["pipeline", "pipe", "pipes", "operator", "function", "polymorphism"]
9 |
10 | [dependencies]
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pipette
2 |
3 |
20 |
21 | A small crate for using pipes in Rust.
22 |
23 | ```rust
24 | use pipette::{Pipeline, pipe};
25 |
26 | let input = 1;
27 |
28 | let output = pipe((
29 | input
30 | |a| a * 2,
31 | |a| a * 3,
32 | |a| a * 4,
33 | |a| a * 5,
34 | |a| a * 6,
35 | |a| a * 7,
36 | |a| a * 8,
37 | ));
38 |
39 | assert_eq!(output, 40_320);
40 | ```
41 |
42 | Pipette is unique:
43 | - Supports polymorphic pipeline sizes
44 | - Integrates well with IDE
45 | - Does not require macros or custom traits
46 |
47 | ## How to use Pipette
48 |
49 | Pipette uses trait-based polymorphism (the `Pipeline` trait) to make it easy to assemble closure-based pipelines in Rust. A single pipeline may consist of up to 12 closures and does not require static typing (ie pipe3, pipe4, pipe5 etc.). Instead, the Pipeline trait will allow any tuple, calling `compute` any any size of pipeline.
50 |
51 | ```rust
52 | fn add_one(a: i32) -> i32 {
53 | a + 1
54 | }
55 |
56 | let r0 = pipe((0, add_one, add_one));
57 | let r1 = pipe((0, add_one, add_one, add_one));
58 | let r2 = pipe((0, add_one, add_one, add_one, add_one));
59 | let r3 = pipe((0, add_one, add_one, add_one, add_one, add_one));
60 |
61 | // lazy pipeline
62 | let add_three = (0, add_one, add_one, add_one);
63 |
64 | add_three.compute();
65 | ```
66 | ## License
67 |
68 | This project is licensed under the [MIT license].
69 |
70 | [MIT license]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
71 |
72 | ### Contribution
73 |
74 | Unless you explicitly state otherwise, any contribution intentionally submitted
75 | for inclusion in Pipette by you, shall be licensed as MIT, without any additional
76 | terms or conditions.
77 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![doc = include_str!("../README.md")]
2 |
3 | ///
4 | ///
5 | /// # Example
6 | /// ```rust
7 | /// let input = 1;
8 | ///
9 | /// let output = pipette::pipe((
10 | /// input
11 | /// |a| a * 2,
12 | /// |a| a * 3,
13 | /// |a| a * 4,
14 | /// |a| a * 5,
15 | /// |a| a * 6,
16 | /// |a| a * 7,
17 | /// |a| a * 8,
18 | /// ));
19 | ///
20 | /// assert_eq!(output, 40_320);
21 | /// ```
22 | pub fn pipe(p: impl Pipeline) -> O {
23 | p.compute()
24 | }
25 |
26 | pub trait Pipeline {
27 | fn compute(self) -> O;
28 | }
29 |
30 | impl Pipeline for (I, A, B)
31 | where
32 | A: FnOnce(I) -> AI,
33 | B: FnOnce(AI) -> O,
34 | {
35 | fn compute(self) -> O {
36 | let (input, a, b) = self;
37 | b(a(input))
38 | }
39 | }
40 |
41 | impl Pipeline for (I, A, B, C)
42 | where
43 | A: FnOnce(I) -> AI,
44 | B: FnOnce(AI) -> BI,
45 | C: FnOnce(BI) -> O,
46 | {
47 | fn compute(self) -> O {
48 | let (input, a, b, c) = self;
49 | c(b(a(input)))
50 | }
51 | }
52 |
53 | impl Pipeline for (I, A, B, C, D)
54 | where
55 | A: FnOnce(I) -> AI,
56 | B: FnOnce(AI) -> BI,
57 | C: FnOnce(BI) -> CI,
58 | D: FnOnce(CI) -> O,
59 | {
60 | fn compute(self) -> O {
61 | let (input, a, b, c, d) = self;
62 | d(c(b(a(input))))
63 | }
64 | }
65 |
66 | impl Pipeline for (I, A, B, C, D, E)
67 | where
68 | A: FnOnce(I) -> AI,
69 | B: FnOnce(AI) -> BI,
70 | C: FnOnce(BI) -> CI,
71 | D: FnOnce(CI) -> DI,
72 | E: FnOnce(DI) -> O,
73 | {
74 | fn compute(self) -> O {
75 | let (input, a, b, c, d, e) = self;
76 | e(d(c(b(a(input)))))
77 | }
78 | }
79 |
80 | impl Pipeline for (I, A, B, C, D, E, F)
81 | where
82 | A: FnOnce(I) -> AI,
83 | B: FnOnce(AI) -> BI,
84 | C: FnOnce(BI) -> CI,
85 | D: FnOnce(CI) -> DI,
86 | E: FnOnce(DI) -> EI,
87 | F: FnOnce(EI) -> O,
88 | {
89 | fn compute(self) -> O {
90 | let (input, a, b, c, d, e, f) = self;
91 | f(e(d(c(b(a(input))))))
92 | }
93 | }
94 |
95 | impl Pipeline for (I, A, B, C, D, E, F, G)
96 | where
97 | A: FnOnce(I) -> AI,
98 | B: FnOnce(AI) -> BI,
99 | C: FnOnce(BI) -> CI,
100 | D: FnOnce(CI) -> DI,
101 | E: FnOnce(DI) -> EI,
102 | F: FnOnce(EI) -> FI,
103 | G: FnOnce(FI) -> O,
104 | {
105 | fn compute(self) -> O {
106 | let (input, a, b, c, d, e, f, g) = self;
107 | g(f(e(d(c(b(a(input)))))))
108 | }
109 | }
110 |
111 | impl Pipeline
112 | for (I, A, B, C, D, E, F, G, H)
113 | where
114 | A: FnOnce(I) -> AI,
115 | B: FnOnce(AI) -> BI,
116 | C: FnOnce(BI) -> CI,
117 | D: FnOnce(CI) -> DI,
118 | E: FnOnce(DI) -> EI,
119 | F: FnOnce(EI) -> FI,
120 | G: FnOnce(FI) -> GI,
121 | H: FnOnce(GI) -> O,
122 | {
123 | fn compute(self) -> O {
124 | let (input, a, b, c, d, e, f, g, h) = self;
125 | h(g(f(e(d(c(b(a(input))))))))
126 | }
127 | }
128 |
129 | impl Pipeline
130 | for (IN, A, B, C, D, E, F, G, H, I)
131 | where
132 | A: FnOnce(IN) -> AI,
133 | B: FnOnce(AI) -> BI,
134 | C: FnOnce(BI) -> CI,
135 | D: FnOnce(CI) -> DI,
136 | E: FnOnce(DI) -> EI,
137 | F: FnOnce(EI) -> FI,
138 | G: FnOnce(FI) -> GI,
139 | H: FnOnce(GI) -> HI,
140 | I: FnOnce(HI) -> OUT,
141 | {
142 | fn compute(self) -> OUT {
143 | let (input, a, b, c, d, e, f, g, h, i) = self;
144 | i(h(g(f(e(d(c(b(a(input)))))))))
145 | }
146 | }
147 |
148 | impl Pipeline
149 | for (IN, A, B, C, D, E, F, G, H, I, J)
150 | where
151 | A: FnOnce(IN) -> AI,
152 | B: FnOnce(AI) -> BI,
153 | C: FnOnce(BI) -> CI,
154 | D: FnOnce(CI) -> DI,
155 | E: FnOnce(DI) -> EI,
156 | F: FnOnce(EI) -> FI,
157 | G: FnOnce(FI) -> GI,
158 | H: FnOnce(GI) -> HI,
159 | I: FnOnce(HI) -> II,
160 | J: FnOnce(II) -> OUT,
161 | {
162 | fn compute(self) -> OUT {
163 | let (input, a, b, c, d, e, f, g, h, i, j) = self;
164 | j(i(h(g(f(e(d(c(b(a(input))))))))))
165 | }
166 | }
167 |
168 | impl Pipeline
169 | for (IN, A, B, C, D, E, F, G, H, I, J, K)
170 | where
171 | A: FnOnce(IN) -> AI,
172 | B: FnOnce(AI) -> BI,
173 | C: FnOnce(BI) -> CI,
174 | D: FnOnce(CI) -> DI,
175 | E: FnOnce(DI) -> EI,
176 | F: FnOnce(EI) -> FI,
177 | G: FnOnce(FI) -> GI,
178 | H: FnOnce(GI) -> HI,
179 | I: FnOnce(HI) -> II,
180 | J: FnOnce(II) -> JI,
181 | K: FnOnce(JI) -> OUT,
182 | {
183 | fn compute(self) -> OUT {
184 | let (input, a, b, c, d, e, f, g, h, i, j, k) = self;
185 | k(j(i(h(g(f(e(d(c(b(a(input)))))))))))
186 | }
187 | }
188 |
189 | #[test]
190 | fn pipe_works() {
191 | fn add_one(a: i32) -> i32 {
192 | a + 1
193 | }
194 |
195 | let r0 = pipe((0, add_one, add_one));
196 |
197 | let r1 = pipe((0, add_one, add_one, add_one));
198 |
199 | let r2 = pipe((0, add_one, add_one, add_one, add_one));
200 |
201 | let r3 = pipe((0, add_one, add_one, add_one, add_one, add_one));
202 |
203 | let r4 = pipe((0, add_one, add_one, add_one, add_one, add_one, add_one));
204 |
205 | let r5 = pipe((
206 | 0, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
207 | ));
208 | let r6 = pipe((
209 | 0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
210 | ));
211 |
212 | let r7 = pipe((
213 | 0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
214 | ));
215 |
216 | let r8 = pipe((
217 | 0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
218 | ));
219 |
220 | let r9 = pipe((
221 | 0, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one, add_one,
222 | add_one, add_one,
223 | ));
224 |
225 | debug_assert_eq!(r0, 2);
226 | debug_assert_eq!(r1, 3);
227 | debug_assert_eq!(r2, 4);
228 | debug_assert_eq!(r3, 5);
229 | debug_assert_eq!(r4, 6);
230 | debug_assert_eq!(r5, 7);
231 | debug_assert_eq!(r6, 8);
232 | debug_assert_eq!(r7, 9);
233 | debug_assert_eq!(r8, 10);
234 | debug_assert_eq!(r9, 11);
235 |
236 | let r9 = pipe((
237 | //
238 | 1_i32,
239 | |a| a * 2,
240 | |a| a * 2,
241 | |a| a * 2,
242 | |a| a * 2,
243 | |a| a * 2,
244 | |a| a * 2,
245 | |a| a * 2,
246 | ));
247 |
248 | debug_assert_eq!(r9, 1024);
249 | }
250 |
--------------------------------------------------------------------------------