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

#[macro_use]
extern crate futures;

/// A macro to create a future that has branched from multiple underlying futures of distinct
/// types.
#[macro_export]
macro_rules! union_future {
    ($name:ident<$item:ty, $err:ty>, $($n:ident => $ft:ty),*) => (
        pub enum $name {
            $( $n($ft) ),*
        }

        impl futures::Future for $name {
            type Item = $item;
            type Error = $err;

            fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
                match *self {
                    $(
                        $name::$n(ref mut f) => {
                            match f.poll() {
                                Ok(futures::Async::Ready(t)) => Ok(futures::Async::Ready(From::from(t))),
                                Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady),
                                Err(e) => Err(From::from(e)),
                            }
                        }
                        ),*
                }
            }
        }

        $(
            impl From<$ft> for $name {
                fn from(other: $ft) -> $name {
                    $name::$n(other)
                }
            })*
    );
}

#[cfg(test)]
#[allow(dead_code)]
mod tests {
    extern crate futures;
    use futures::*;
    use futures::future::*;

    #[derive(PartialEq, Debug, Eq)]
    pub enum Error {
        Fail,
        BigFail,
    }

    #[derive(PartialEq, Debug, Eq)]
    pub struct OtherError {
        op: u64
    }

    impl From<OtherError> for Error {
        fn from(_: OtherError) -> Error {
            Error::BigFail
        }
    }

    #[test]
    fn same_types() {
        union_future!(TestFut<u64, Error>,
                Forever => Empty<u64, Error>,
                Immediate => FutureResult<u64, Error>);

        let mut a: TestFut = empty::<u64, Error>().into();
        assert_eq!(Ok(Async::NotReady), a.poll());
        let mut b: TestFut = ok::<u64, Error>(5).into();
        assert_eq!(Ok(Async::Ready(5u64)), b.poll());
    }

    #[test]
    fn different_item_types() {
        union_future!(TestFut<f64, Error>,
                Number => FutureResult<u32, Error>,
                Floating => FutureResult<f32, Error>);

        let mut a: TestFut = ok::<u32, Error>(5u32).into();
        assert_eq!(Ok(Async::Ready(5f64)), a.poll());
        let mut b: TestFut = ok::<f32, Error>(5.25f32).into();
        assert_eq!(Ok(Async::Ready(5.25f64)), b.poll());
    }

    #[test]
    fn different_err_types() {
        union_future!(TestFut<f64, Error>,
                Number => FutureResult<u32, Error>,
                Floating => FutureResult<f32, OtherError>);

        let mut a: TestFut = ok::<u32, Error>(5u32).into();
        assert_eq!(Ok(Async::Ready(5f64)), a.poll());
        let mut b: TestFut = ok::<f32, OtherError>(5.25f32).into();
        assert_eq!(Ok(Async::Ready(5.25f64)), b.poll());
    }
}