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
//! This crate contains a simple macro for creating `Future`-implementing enums containing futures.
//!
//! It can be used as an alternative to `futures::Either` if more than two variants are desired or
//! if one wants to give them more meaningful names. The enum is generic, in order to allow use
//! with complicated or unname-able types (combinators, closures, impl Future).
//!
//! This macro doesn't allow for creating `pub` enums, documenting them or deriving any other
//! trait. This should be fine in most cases, as it's intended to be used mainly in `match`
//! expressions returning different futures.
//!
//! Derive macro would certainly provide more flexibility, but it'd also be more complicated and
//! this simple macro solves my problem. If anyone is interested in contributing proc macro
//! version, I'd be very happy to merge a PR!
//!
//! This crate is `no_std` and pre-2018-compatible, since it doesn't need anything fancy from
//! neither `std` nor 2018 edition.
//!
//! # Example
//!
//! ```
//! #[macro_use]
//! extern crate enum_future;
//!
//! use enum_future::futures::future::{Future, ok, err};
//!
//! fn make_future(input: u64) -> impl Future<Item=bool, Error=&'static str> {
//!     enum_future!(Ret, Zero, One, Other);
//!
//!     match input {
//!         0 => Ret::Zero(ok(false)),
//!         1 => Ret::One(ok(true)),
//!         _ => Ret::Other(err("Invalid input")),
//!     }
//! }
//!
//! fn main() {
//!     assert_eq!(make_future(42).wait(), Err("Invalid input"));
//! }
//! ```

#![no_std]

#[cfg(test)]
extern crate std;
pub extern crate futures;

/// Creates a generic enum of type `$name` with variants `$first, $($other,)*`, each containing a
/// single value of possibly different type. The enum implements `Future` by delegating to the
/// value of appropriate variant.
#[macro_export]
macro_rules! enum_future {
    ($name:ident, $first:ident, $($other:ident),*) => {
        enum $name<$first, $($other,)*> {
            $first($first),
            $(
                $other($other),
            )*
        }

        impl<$first, $($other,)*> $crate::futures::Future for $name<$first, $($other,)*> where 
            $first: $crate::futures::Future,
            $($other: $crate::futures::Future<Item=$first::Item, Error=$first::Error>,)*
        {
            type Item=$first::Item;
            type Error=$first::Error;

            fn poll(&mut self) -> $crate::futures::Poll<Self::Item, Self::Error> {
                match self {
                    $name::$first(future) => future.poll(),
                    $(
                        $name::$other(future) => future.poll(),
                    )*
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    #[allow(unused)]
    fn it_works() {
        use ::futures::Future;

        enum_future!(TestEnum, Foo, Bar, Baz);

        let test: TestEnum<_, futures::future::FutureResult<_, ()>, futures::future::FutureResult<_, _>> = TestEnum::Foo(::futures::future::ok("Hello world!"));
        assert_eq!(test.wait().unwrap(), "Hello world!");
    }
}