overture_core/
pipe.rs

1// Forward composition of functions.
2// Equivalent to Swift's pipe functions
3
4/// Forward composition of two functions.
5/// Equivalent to Swift's pipe<A, B, C>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C
6pub fn pipe<A, B, C>(
7    f: impl Fn(A) -> B + 'static,
8    g: impl Fn(B) -> C + 'static,
9) -> impl Fn(A) -> C {
10    move |a| g(f(a))
11}
12
13// Helper function for pipe2 (since we don't have it in the API)
14pub fn pipe2<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C
15where
16    F: Fn(A) -> B + 'static,
17    G: Fn(B) -> C + 'static,
18{
19    move |a| g(f(a))
20}
21
22/// Forward composition of three functions.
23/// Equivalent to Swift's pipe<A, B, C, D>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C, _ h: @escaping (C) -> D) -> (A) -> D
24pub fn pipe3<A, B, C, D>(
25    f: impl Fn(A) -> B + 'static,
26    g: impl Fn(B) -> C + 'static,
27    h: impl Fn(C) -> D + 'static,
28) -> impl Fn(A) -> D {
29    move |a| h(g(f(a)))
30}
31
32/// Forward composition of four functions.
33/// Equivalent to Swift's pipe<A, B, C, D, E>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C, _ h: @escaping (C) -> D, _ i: @escaping (D) -> E) -> (A) -> E
34pub fn pipe4<A, B, C, D, E>(
35    f: impl Fn(A) -> B + 'static,
36    g: impl Fn(B) -> C + 'static,
37    h: impl Fn(C) -> D + 'static,
38    i: impl Fn(D) -> E + 'static,
39) -> impl Fn(A) -> E {
40    move |a| i(h(g(f(a))))
41}
42
43/// Forward composition of five functions.
44/// Equivalent to Swift's pipe<A, B, C, D, E, F>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C, _ h: @escaping (C) -> D, _ i: @escaping (D) -> E, _ j: @escaping (E) -> F) -> (A) -> F
45pub fn pipe5<A, B, C, D, E, F>(
46    f: impl Fn(A) -> B + 'static,
47    g: impl Fn(B) -> C + 'static,
48    h: impl Fn(C) -> D + 'static,
49    i: impl Fn(D) -> E + 'static,
50    j: impl Fn(E) -> F + 'static,
51) -> impl Fn(A) -> F {
52    move |a| j(i(h(g(f(a)))))
53}
54
55/// Forward composition of six functions.
56/// Equivalent to Swift's pipe<A, B, C, D, E, F, G>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C, _ h: @escaping (C) -> D, _ i: @escaping (D) -> E, _ j: @escaping (E) -> F, _ k: @escaping (F) -> G) -> (A) -> G
57pub fn pipe6<A, B, C, D, E, F, G>(
58    f: impl Fn(A) -> B + 'static,
59    g: impl Fn(B) -> C + 'static,
60    h: impl Fn(C) -> D + 'static,
61    i: impl Fn(D) -> E + 'static,
62    j: impl Fn(E) -> F + 'static,
63    k: impl Fn(F) -> G + 'static,
64) -> impl Fn(A) -> G {
65    move |a| k(j(i(h(g(f(a))))))
66}
67
68// Throwing variants (using Result for error handling)
69
70/// Forward composition of two throwing functions.
71/// Equivalent to Swift's pipe<A, B, C>(_ f: @escaping (A) throws -> B, _ g: @escaping (B) throws -> C) -> (A) throws -> C
72pub fn pipe_throwing<A, B, C, E>(
73    f: impl Fn(A) -> Result<B, E> + 'static,
74    g: impl Fn(B) -> Result<C, E> + 'static,
75) -> impl Fn(A) -> Result<C, E> {
76    move |a| f(a).and_then(|b| g(b))
77}
78
79/// Forward composition of three throwing functions.
80/// Equivalent to Swift's pipe<A, B, C, D>(_ f: @escaping (A) throws -> B, _ g: @escaping (B) throws -> C, _ h: @escaping (C) throws -> D) -> (A) throws -> D
81pub fn pipe3_throwing<A, B, C, D, E>(
82    f: impl Fn(A) -> Result<B, E> + 'static,
83    g: impl Fn(B) -> Result<C, E> + 'static,
84    h: impl Fn(C) -> Result<D, E> + 'static,
85) -> impl Fn(A) -> Result<D, E> {
86    move |a| f(a).and_then(|b| g(b)).and_then(|c| h(c))
87}
88
89/// Forward composition of four throwing functions.
90/// Equivalent to Swift's pipe<A, B, C, D, E>(_ f: @escaping (A) throws -> B, _ g: @escaping (B) throws -> C, _ h: @escaping (C) throws -> D, _ i: @escaping (D) throws -> E) -> (A) throws -> E
91pub fn pipe4_throwing<A, B, C, D, E, F>(
92    f: impl Fn(A) -> Result<B, F> + 'static,
93    g: impl Fn(B) -> Result<C, F> + 'static,
94    h: impl Fn(C) -> Result<D, F> + 'static,
95    i: impl Fn(D) -> Result<E, F> + 'static,
96) -> impl Fn(A) -> Result<E, F> {
97    move |a| {
98        f(a).and_then(|b| g(b))
99            .and_then(|c| h(c))
100            .and_then(|d| i(d))
101    }
102}
103
104/// Forward composition of five throwing functions.
105/// Equivalent to Swift's pipe<A, B, C, D, E, F>(_ f: @escaping (A) throws -> B, _ g: @escaping (B) throws -> C, _ h: @escaping (C) throws -> D, _ i: @escaping (D) throws -> E, _ j: @escaping (E) throws -> F) -> (A) throws -> F
106pub fn pipe5_throwing<A, B, C, D, E, F, G>(
107    f: impl Fn(A) -> Result<B, G> + 'static,
108    g: impl Fn(B) -> Result<C, G> + 'static,
109    h: impl Fn(C) -> Result<D, G> + 'static,
110    i: impl Fn(D) -> Result<E, G> + 'static,
111    j: impl Fn(E) -> Result<F, G> + 'static,
112) -> impl Fn(A) -> Result<F, G> {
113    move |a| {
114        f(a).and_then(|b| g(b))
115            .and_then(|c| h(c))
116            .and_then(|d| i(d))
117            .and_then(|e| j(e))
118    }
119}
120
121/// Forward composition of six throwing functions.
122/// Equivalent to Swift's pipe<A, B, C, D, E, F, G>(_ f: @escaping (A) throws -> B, _ g: @escaping (B) throws -> C, _ h: @escaping (C) throws -> D, _ i: @escaping (D) throws -> E, _ j: @escaping (E) throws -> F, _ k: @escaping (F) throws -> G) -> (A) throws -> G
123pub fn pipe6_throwing<A, B, C, D, E, F, G, H>(
124    f: impl Fn(A) -> Result<B, H> + 'static,
125    g: impl Fn(B) -> Result<C, H> + 'static,
126    h: impl Fn(C) -> Result<D, H> + 'static,
127    i: impl Fn(D) -> Result<E, H> + 'static,
128    j: impl Fn(E) -> Result<F, H> + 'static,
129    k: impl Fn(F) -> Result<G, H> + 'static,
130) -> impl Fn(A) -> Result<G, H> {
131    move |a| {
132        f(a).and_then(|b| g(b))
133            .and_then(|c| h(c))
134            .and_then(|d| i(d))
135            .and_then(|e| j(e))
136            .and_then(|f| k(f))
137    }
138}
139
140// Legacy aliases for backward compatibility
141pub use pipe_throwing as pipe_result;
142pub use pipe3_throwing as pipe_result3;
143
144// #[macro_export]
145// macro_rules! pipe_macro {
146//     ($f:expr, $g:expr) => {
147//         crate::pipe::pipe($f, $g)
148//     };
149//     ($f:expr, $g:expr, $h:expr) => {
150//         crate::pipe::pipe3($f, $g, $h)
151//     };
152//     ($f:expr, $g:expr, $h:expr, $i:expr) => {
153//         crate::pipe::pipe4($f, $g, $h, $i)
154//     };
155//     ($f:expr, $g:expr, $h:expr, $i:expr, $j:expr) => {
156//         crate::pipe::pipe5($f, $g, $h, $i, $j)
157//     };
158//     ($f:expr, $g:expr, $h:expr, $i:expr, $j:expr, $k:expr) => {
159//         crate::pipe::pipe6($f, $g, $h, $i, $j, $k)
160//     };
161// }
162
163// #[macro_export]
164// macro_rules! pipe_result {
165//     ($f:expr, $g:expr) => {
166//         crate::pipe::pipe_result($f, $g)
167//     };
168//     ($f:expr, $g:expr, $h:expr) => {
169//         crate::pipe::pipe_result3($f, $g, $h)
170//     };
171// }