and_then2/
lib.rs

1//! [![pipeline status](https://gitlab.com/mexus/and-then2/badges/master/pipeline.svg)](https://gitlab.com/mexus/and-then2/commits/master)
2//! [![crates.io](https://img.shields.io/crates/v/and-then2.svg)](https://crates.io/crates/and-then2)
3//! [![docs.rs](https://docs.rs/and-then2/badge.svg)](https://docs.rs/and-then2)
4//!
5//! [[Master docs]](https://mexus.gitlab.io/and-then2/and_then2/), [[Release docs]](https://docs.rs/and-then2/)
6//!
7//! # And Then II
8//!
9//! An alternative to a traditional `Future::and_then` combinator.
10//!
11//! This is an experimental crate! Be warned :)
12//!
13//! More info is coming...
14//! ## License
15//!
16//! Licensed under either of
17//!
18//!  * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
19//!  * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
20//!
21//! at your option.
22//!
23//! ### Contribution
24//!
25//! Unless you explicitly state otherwise, any contribution intentionally submitted
26//! for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
27//! additional terms or conditions.
28
29extern crate futures;
30
31use futures::{Async, Future, IntoFuture, Poll};
32use std::mem;
33
34enum StateMachine<Original, Resulting, F> {
35    First(F, Original),
36    Second(Resulting),
37    EmptyState,
38}
39
40/// AndThen-like futures combinator. See [`FutureExt`](trait.FutureExt.html) for details.
41pub struct AndThen2<Original, Resulting, F>(StateMachine<Original, Resulting, F>);
42
43impl<Original, Intermediate, F> Future for StateMachine<Original, Intermediate::Future, F>
44where
45    Original: Future,
46    Intermediate: IntoFuture,
47    F: FnOnce(Original::Item) -> Intermediate,
48{
49    type Item = Result<Intermediate::Item, Intermediate::Error>;
50    type Error = Original::Error;
51
52    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
53        loop {
54            let previous_state = mem::replace(self, StateMachine::EmptyState);
55            let new_state = match previous_state {
56                StateMachine::First(f, mut original) => match original.poll()? {
57                    Async::Ready(x) => StateMachine::Second(f(x).into_future()),
58                    Async::NotReady => {
59                        mem::replace(self, StateMachine::First(f, original));
60                        return Ok(Async::NotReady);
61                    }
62                },
63                StateMachine::Second(mut resulting) => match resulting.poll() {
64                    Err(e) => return Ok(Async::Ready(Err(e))),
65                    Ok(Async::Ready(res)) => return Ok(Async::Ready(Ok(res))),
66                    Ok(Async::NotReady) => {
67                        mem::replace(self, StateMachine::Second(resulting));
68                        return Ok(Async::NotReady);
69                    }
70                },
71                StateMachine::EmptyState => panic!("poll() called after completion"),
72            };
73            mem::replace(self, new_state);
74        }
75    }
76}
77
78impl<Original, Intermediate, F> Future for AndThen2<Original, Intermediate::Future, F>
79where
80    Original: Future,
81    Intermediate: IntoFuture,
82    F: FnOnce(Original::Item) -> Intermediate,
83{
84    type Item = Result<Intermediate::Item, Intermediate::Error>;
85    type Error = Original::Error;
86
87    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
88        self.0.poll()
89    }
90}
91
92/// An extension trait that provides a convenient method `and_then2`.
93pub trait FutureExt: Future {
94    /// Creates a future object that resolves into an item that contains a `Result` of a
95    /// computation of the provided closure `f`.
96    ///
97    /// It is very similar to a traditional `Future::and_then`, but differs in an item's type.
98    ///
99    /// Might come in handy when you have futures with different error types and you don't want to
100    /// collect them into an enum.
101    fn and_then2<F, B>(self, f: F) -> AndThen2<Self, B::Future, F>
102    where
103        B: IntoFuture,
104        F: FnOnce(Self::Item) -> B,
105        Self: Sized,
106    {
107        AndThen2(StateMachine::First(f, self))
108    }
109}
110
111impl<T: Future> FutureExt for T {}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[derive(Debug, PartialEq)]
118    struct Err1;
119    #[derive(Debug, PartialEq)]
120    struct Err2;
121
122    fn plus_one_ok(x: usize) -> impl IntoFuture<Item = usize, Error = Err2> {
123        futures::future::ok(x + 1)
124    }
125
126    fn just_err(_: usize) -> impl IntoFuture<Item = usize, Error = Err2> {
127        futures::future::err(Err2)
128    }
129
130    #[test]
131    fn test_ok_ok() {
132        let f1 = futures::future::ok::<_, Err1>(1);
133        let combined = f1.and_then2(plus_one_ok);
134        assert_eq!(Ok(Ok(2)), combined.wait());
135    }
136
137    #[test]
138    fn test_ok_err() {
139        let f1 = futures::future::ok::<_, Err1>(1);
140        let combined = f1.and_then2(just_err);
141        assert_eq!(Ok(Err(Err2)), combined.wait());
142    }
143
144    #[test]
145    fn test_err_ok() {
146        let f1 = futures::future::err(Err1);
147        let combined = f1.and_then2(plus_one_ok);
148        assert_eq!(Err(Err1), combined.wait());
149    }
150
151    #[test]
152    fn test_err_err() {
153        let f1 = futures::future::err(Err1);
154        let combined = f1.and_then2(just_err);
155        assert_eq!(Err(Err1), combined.wait());
156    }
157}