├── .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 |
4 | 5 | 6 | Crates.io version 8 | 9 | 10 | 11 | Download 13 | 14 | 15 | 16 | docs.rs docs 18 | 19 |
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 | --------------------------------------------------------------------------------