Skip to main content

graphitepdf_utils/
compose.rs

1use std::future::Future;
2use std::pin::Pin;
3
4pub fn compose<F, G, A, B, C>(f: F, g: G) -> impl Fn(A) -> C
5where
6    F: Fn(B) -> C,
7    G: Fn(A) -> B,
8{
9    move |input| f(g(input))
10}
11
12pub fn async_compose<F, G, FutF, FutG, A, B, C>(
13    f: F,
14    g: G,
15) -> impl Fn(A) -> Pin<Box<dyn Future<Output = C>>>
16where
17    F: Fn(B) -> FutF + Copy + 'static,
18    G: Fn(A) -> FutG + Copy + 'static,
19    FutF: Future<Output = C> + 'static,
20    FutG: Future<Output = B> + 'static,
21    A: 'static,
22{
23    move |input| {
24        Box::pin(async move {
25            let intermediate = g(input).await;
26            f(intermediate).await
27        })
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34    use std::future::{Ready, ready};
35
36    #[test]
37    fn composes_sync_functions_right_to_left() {
38        let add_one = |value| value + 1;
39        let double = |value| value * 2;
40
41        let function = compose(double, add_one);
42
43        assert_eq!(function(5), 12);
44    }
45
46    #[test]
47    fn composes_async_functions_right_to_left() {
48        let add_async = |value| ready(value + 1);
49        let double_async = |value| -> Ready<i32> { ready(value * 2) };
50
51        let function = async_compose(double_async, add_async);
52
53        assert_eq!(pollster(function(5)), 12);
54    }
55
56    fn pollster<F>(future: F) -> F::Output
57    where
58        F: Future,
59    {
60        use std::task::{Context, Poll, Waker};
61
62        let waker = Waker::noop();
63        let mut context = Context::from_waker(waker);
64        let mut future = std::pin::pin!(future);
65
66        match future.as_mut().poll(&mut context) {
67            Poll::Ready(output) => output,
68            Poll::Pending => panic!("expected test future to complete immediately"),
69        }
70    }
71}