try_future/lib.rs
1//! This crate aims to provide a convenient short-hand for returning early
2//! from `futures`-based functions.
3//!
4//! The general pattern it supports is where before a function performs
5//! an asynchronous task, it does some work that might result in an early
6//! termination, for example:
7//!
8//! - certain parsing or validation logic that might fail, and upon which
9//! the function should return immediately with an error
10//! - some local cache lookup or other optimization that might render the
11//! asynchronous task unnecessary, and where the function should instead
12//! return immediately with a value
13//!
14//! To that end, the [`TryFuture`](TryFuture) struct implements a future which can
15//! either resolve immediately with a value or an error, or alternatively wrap another
16//! future performing some asynchronous task.
17//!
18//! The equivalent of [`try!`](try!) or the `?` operator is provided by the
19//! [`try_future!`](try_future!) and [`try_future_box!`](try_future_box!) macros:
20//!
21//! - `try_future!` will inspect the `Result` passed to it, and will exit
22//! the current function by returning a `TryFuture` when it finds an `Err`. Otherwise
23//! it will unwrap the result and pass back the value inside the result.
24//!
25//! - `try_future_box!` works in the same way, except that when encountering an `Err`
26//! it will return a boxed future.
27//!
28//! > **Please note**: The `try_future!` and `try_future_box!` macros only accept inputs
29//! > of type `Result`. Alas, the equivalent of `async`/`await` is not yet possible in
30//! > stable rust (however if you don't shy away from using nightly, you could take a look
31//! > at the [`futures-await`](https://github.com/alexcrichton/futures-await) project).
32//!
33//! # Examples
34//!
35//! ## Using `impl Future<_>`
36//!
37//! ```rust,ignore
38//! #[macro_use] extern crate try_future;
39//!
40//! fn make_request<C: Connect>(target: &str, client: &Client<C>) ->
41//! impl Future<Item=Response, Error=Error>
42//! {
43//! let uri = try_future!(target.parse::<Uri>());
44//!
45//! client.get(uri).into()
46//! }
47//! ```
48//!
49//! ## Using `Box<Future<_>>`
50//!
51//! ```rust,ignore
52//! #[macro_use] extern crate try_future;
53//!
54//! fn make_request<C: Connect>(target: &str, client: &Client<C>) ->
55//! Box<Future<Item=Response, Error=Error>>
56//! {
57//! let uri = try_future_box!(target.parse::<Uri>());
58//!
59//! Box::new(client.get(uri))
60//! }
61//! ```
62
63extern crate futures;
64
65use std::fmt::{Debug, Formatter, Result as FmtResult};
66
67use futures::{Future, IntoFuture, Poll};
68
69#[doc(hidden)]
70pub use futures::future::err;
71
72/// Future which can either resolve immediately with a value or an error,
73/// or alternatively wrap another future performing some asynchronous task.
74///
75/// The [`From`](From) impl on `TryFuture` can be used to wrap another future.
76pub struct TryFuture<F: Future> {
77 inner: TryFutureInner<F>
78}
79
80impl<F: Future + Debug> Debug for TryFuture<F> {
81 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
82 fmt.debug_struct("TryFuture").finish()
83 }
84}
85
86#[derive(Debug)]
87enum TryFutureInner<F: Future> {
88 Result(::futures::future::FutureResult<F::Item, F::Error>),
89 Future(F)
90}
91
92impl<F: Future> TryFuture<F> {
93 fn from_result(res: Result<F::Item, F::Error>) -> TryFuture<F> {
94 TryFuture {
95 inner: TryFutureInner::Result(res.into_future())
96 }
97 }
98
99 /// Create a `TryFuture` future that immediately resolves
100 /// with the specified value.
101 pub fn from_ok(item: F::Item) -> TryFuture<F> {
102 TryFuture::from_result(Ok(item))
103 }
104
105 /// Create a `TryFuture` future that immediately rejects
106 /// with the specified error.
107 pub fn from_error(err: F::Error) -> TryFuture<F> {
108 TryFuture::from_result(Err(err))
109 }
110}
111
112impl<F: IntoFuture> From<F> for TryFuture<F::Future> {
113 fn from(future: F) -> TryFuture<F::Future> {
114 TryFuture {
115 inner: TryFutureInner::Future(future.into_future())
116 }
117 }
118}
119
120impl<F: Future> Future for TryFuture<F> {
121 type Item = F::Item;
122 type Error = F::Error;
123
124 fn poll(&mut self) -> Poll<F::Item, F::Error> {
125 match self.inner {
126 TryFutureInner::Result(ref mut future) => future.poll(),
127 TryFutureInner::Future(ref mut future) => future.poll()
128 }
129 }
130}
131
132/// Equivalent of `try! returning a [`TryFuture`](TryFuture).
133#[macro_export]
134macro_rules! try_future {
135 ($expression:expr) => (
136 match $expression {
137 Err(err) => {
138 return $crate::TryFuture::from_error(err.into());
139 },
140 Ok(value) => value
141 }
142 )
143}
144
145/// Equivalent of `try!` returning a `Box<Future<_>>`.
146#[macro_export]
147macro_rules! try_future_box {
148 ($expression:expr) => (
149 match $expression {
150 Err(err) => {
151 return Box::new($crate::err(err.into()));
152 },
153 Ok(value) => value
154 }
155 )
156}
157
158#[cfg(test)]
159mod tests {
160 use futures::{future, Future};
161
162 use super::TryFuture;
163
164 #[test]
165 fn test_from_ok() {
166 let future = TryFuture::<future::Empty<u32, ()>>::from_ok(42);
167 assert_eq!(42, future.wait().unwrap());
168 }
169
170 #[test]
171 fn test_from_error() {
172 let future = TryFuture::<future::Empty<u32, ()>>::from_error(());
173 assert_eq!((), future.wait().err().unwrap());
174 }
175
176 #[test]
177 fn test_ok_into() {
178 let future: TryFuture<_> = future::ok::<u32, ()>(42).into();
179 assert_eq!(42, future.wait().unwrap());
180 }
181
182 #[test]
183 fn test_err_into() {
184 let future: TryFuture<_> = future::err::<u32, ()>(()).into();
185 assert_eq!((), future.wait().err().unwrap());
186 }
187
188 #[test]
189 fn test_try_future() {
190 use std::io;
191
192 struct CustomError(io::Error);
193
194 impl From<io::Error> for CustomError {
195 fn from(err: io::Error) -> CustomError {
196 CustomError(err)
197 }
198 }
199
200 let future = future::lazy(|| {
201 try_future!(Err(io::Error::new(io::ErrorKind::Other, "oh no!")));
202
203 future::ok::<_, CustomError>(()).into()
204 });
205
206 assert_eq!("oh no!", future.wait().err().unwrap().0.to_string());
207 }
208
209 #[test]
210 fn test_try_future_box() {
211 use std::io;
212
213 struct CustomError(io::Error);
214
215 impl From<io::Error> for CustomError {
216 fn from(err: io::Error) -> CustomError {
217 CustomError(err)
218 }
219 }
220
221 let future = future::lazy(|| {
222 try_future_box!(Err(io::Error::new(io::ErrorKind::Other, "oh no!")));
223
224 Box::new(future::ok::<_, CustomError>(()))
225 });
226
227 assert_eq!("oh no!", future.wait().err().unwrap().0.to_string());
228 }
229}