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}