union_future/
lib.rs

1//! Explicit and efficient future that results from a branched future.
2//!
3//! The `union_future` macro creates a future derived from a branch of different underlying
4//! futures. The macro can prevent unnecessary boxing of futures when the code can branch
5//! into multiple future types.
6//!
7//! The macro works by exposing an enum that implements the Future trait, where the underlying
8//! future drives the polling behavior. The variants of the enum can have different underlying
9//! state machines (types that implement the `Future` trait).
10//!
11//! Additionally, the underlying branch state machines can have *different* Item types that are
12//! mapped to the `union_future` future's Item type via the `From` trait.
13//!
14//! Also, as an added bonus, the macro will derive the `From` trait for the underlying state
15//! machines in order to make the branched code clean.
16//!
17//! ## Installation
18//!
19//! Add this to your `Cargo.toml`:
20//!
21//! ```toml
22//! [dependencies]
23//! union-future = "0.1"
24//! futures = "0.1"
25//! ```
26//! ## Examples
27//!
28//! The basic usage of the macro uses the same Item type from different underlying
29//! futures. For example, if you have a locally cached version otherwise the code
30//! will query the database:
31//!
32//! ```
33//! #[macro_use]
34//! extern crate union_future;
35//! extern crate futures;
36//!
37//! use futures::*;
38//! use futures::future::*;
39//!
40//!
41//! // Macro will create the enum and necessary trait implementations
42//! // for the QueryFuture. This enum will have 2 variants: Cached and Db.
43//! union_future!(QueryFuture<u64, DbError>,
44//!       Cached => FutureResult<u64, DbError>,
45//!       Db => DbQueryFuture<u64>);
46//!
47//! // Example code that branches, using the future created by the macro
48//! pub fn query(db: &Db, key: &str) -> QueryFuture {
49//!     // this example shows multiple ways the QueryFuture can be constructed:
50//!     // either by the explicit enum variant or by using the From/Into traits
51//!     if let Some(cached_val) = check_local_cache(key) {
52//!         QueryFuture::Cached(ok(cached_val))
53//!     } else {
54//!         query_db(db, key).into()
55//!     }
56//! }
57//!
58//! fn check_local_cache(key: &str) -> Option<u64> {
59//!     // ...
60//!     # panic!("Unimplemented")
61//! }
62//!
63//! fn query_db(db: &Db, key: &str) -> DbQueryFuture<u64> {
64//!     // ...
65//!     # panic!("Unimplemented")
66//! }
67//!
68//! # pub struct DbError {
69//! # }
70//! # pub struct Db {
71//! # }
72//! # pub type DbQueryFuture<T> = Empty<T, DbError>;
73//! # fn main() {}
74//! ```
75//!
76//! You could, however, have a future that can be mapped into the future result type
77//! with the `From` trait:
78//!
79//! ```
80//! # #[macro_use]
81//! # extern crate union_future;
82//! # extern crate futures;
83//! # use futures::*;
84//! # use futures::future::*;
85//! pub enum RedisValue {
86//!     Null,
87//!     Integer(i64),
88//!     Bulk(String),
89//! }
90//!
91//! // Implementing the From trait allows the underlying futures to expose
92//! // different Item types transparently
93//!
94//! impl From<()> for RedisValue {
95//!     fn from(_: ()) -> RedisValue {
96//!         RedisValue::Null
97//!     }
98//! }
99//!
100//! impl From<i64> for RedisValue {
101//!     fn from(other: i64) -> RedisValue {
102//!         RedisValue::Integer(other)
103//!     }
104//! }
105//!
106//! impl From<String> for RedisValue {
107//!     fn from(other: String) -> RedisValue {
108//!         RedisValue::Bulk(other)
109//!     }
110//! }
111//!
112//! union_future!(RedisValueFuture<RedisValue, DbError>,
113//!       Pong => FutureResult<(), DbError>,
114//!       IntegerQuery => DbQueryFuture<i64>,
115//!       StringQuery => DbQueryFuture<String>);
116//!
117//! # pub struct DbError {
118//! # }
119//! # pub struct MemDb {
120//! # }
121//! # pub type DbQueryFuture<T> = Empty<T, DbError>;
122//! # fn main() {}
123//! ```
124
125#[macro_use]
126extern crate futures;
127
128/// A macro to create a future that has branched from multiple underlying futures of distinct
129/// types.
130#[macro_export]
131macro_rules! union_future {
132    ($name:ident<$item:ty, $err:ty>, $($n:ident => $ft:ty),*) => (
133        pub enum $name {
134            $( $n($ft) ),*
135        }
136
137        impl futures::Future for $name {
138            type Item = $item;
139            type Error = $err;
140
141            fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
142                match *self {
143                    $(
144                        $name::$n(ref mut f) => {
145                            match f.poll() {
146                                Ok(futures::Async::Ready(t)) => Ok(futures::Async::Ready(From::from(t))),
147                                Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady),
148                                Err(e) => Err(From::from(e)),
149                            }
150                        }
151                        ),*
152                }
153            }
154        }
155
156        $(
157            impl From<$ft> for $name {
158                fn from(other: $ft) -> $name {
159                    $name::$n(other)
160                }
161            })*
162    );
163}
164
165#[cfg(test)]
166#[allow(dead_code)]
167mod tests {
168    extern crate futures;
169    use futures::*;
170    use futures::future::*;
171
172    #[derive(PartialEq, Debug, Eq)]
173    pub enum Error {
174        Fail,
175        BigFail,
176    }
177
178    #[derive(PartialEq, Debug, Eq)]
179    pub struct OtherError {
180        op: u64
181    }
182
183    impl From<OtherError> for Error {
184        fn from(_: OtherError) -> Error {
185            Error::BigFail
186        }
187    }
188
189    #[test]
190    fn same_types() {
191        union_future!(TestFut<u64, Error>,
192                Forever => Empty<u64, Error>,
193                Immediate => FutureResult<u64, Error>);
194
195        let mut a: TestFut = empty::<u64, Error>().into();
196        assert_eq!(Ok(Async::NotReady), a.poll());
197        let mut b: TestFut = ok::<u64, Error>(5).into();
198        assert_eq!(Ok(Async::Ready(5u64)), b.poll());
199    }
200
201    #[test]
202    fn different_item_types() {
203        union_future!(TestFut<f64, Error>,
204                Number => FutureResult<u32, Error>,
205                Floating => FutureResult<f32, Error>);
206
207        let mut a: TestFut = ok::<u32, Error>(5u32).into();
208        assert_eq!(Ok(Async::Ready(5f64)), a.poll());
209        let mut b: TestFut = ok::<f32, Error>(5.25f32).into();
210        assert_eq!(Ok(Async::Ready(5.25f64)), b.poll());
211    }
212
213    #[test]
214    fn different_err_types() {
215        union_future!(TestFut<f64, Error>,
216                Number => FutureResult<u32, Error>,
217                Floating => FutureResult<f32, OtherError>);
218
219        let mut a: TestFut = ok::<u32, Error>(5u32).into();
220        assert_eq!(Ok(Async::Ready(5f64)), a.poll());
221        let mut b: TestFut = ok::<f32, OtherError>(5.25f32).into();
222        assert_eq!(Ok(Async::Ready(5.25f64)), b.poll());
223    }
224}