future_utils/
future_ext.rs

1use std::fmt::Display;
2use std::any::Any;
3use std::time::{Instant, Duration};
4use futures::Future;
5use log::LogLevel;
6use void::Void;
7
8use log_error::LogError;
9use until::Until;
10use infallible::Infallible;
11use finally::Finally;
12use with_timeout::WithTimeout;
13use first_ok2::FirstOk2;
14use while_driving::WhileDriving;
15use resume_unwind::ResumeUnwind;
16use BoxFuture;
17use BoxSendFuture;
18
19/// Extension trait for `Future`.
20pub trait FutureExt: Future + Sized {
21    /// Wraps a future into a boxed future, making type-checking easier at the expense of an extra
22    /// layer of indirection at runtime.
23    fn into_boxed(self) -> BoxFuture<Self::Item, Self::Error>
24    where
25        Self: 'static
26    {
27        Box::new(self)
28    }
29
30    fn into_send_boxed(self) -> BoxSendFuture<Self::Item, Self::Error>
31    where
32        Self: Send + 'static,
33    {
34        Box::new(self)
35    }
36
37    /// Run this future until some condition is met. If `condition` resolves before `self` then
38    /// `None` is returned.
39    ///
40    /// # Example
41    /// ```rust
42    /// let my_future_with_timeout = my_future.until(Delay::new(Instant::now() + Duration::from_secs(1)));
43    /// ```
44    fn until<C>(self, condition: C) -> Until<Self, C>
45    where
46        C: Future<Item=()>,
47        Self::Error: From<C::Error>
48    {
49        Until::new(self, condition)
50    }
51
52    /// For futures which can't fail (ie. which have error type `Void`), cast the error type to
53    /// some inferred type.
54    fn infallible<E>(self) -> Infallible<Self, E>
55    where
56        Self: Future<Error=Void>
57    {
58        Infallible::new(self)
59    }
60
61    /// Take a future which returns `()` and log its error if it fails. The returned future cannot
62    /// fail and will always resolve to `()`.
63    fn log_error(self, level: LogLevel, description: &'static str) -> LogError<Self>
64    where
65        Self: Future<Item=()>,
66        Self::Error: Display,
67    {
68        LogError::new(self, level, description)
69    }
70
71    /// Executes the future and runs the provided callback when the future finishes. The callback
72    /// will also be run if the entire future is dropped.
73    fn finally<D>(self, on_drop: D) -> Finally<Self, D>
74    where
75        D: FnOnce()
76    {
77        Finally::new(self, on_drop)
78    }
79
80    /// Runs the future for the given duration, returning its value in an option, or returning
81    /// `None` if the timeout expires.
82    fn with_timeout(self, duration: Duration) -> WithTimeout<Self> {
83        WithTimeout::new(self, duration)
84    }
85
86    /// Runs the future until the given instant, returning its value in an option, or returning
87    /// `None` if the timeout expires.
88    fn with_timeout_at(self, instant: Instant) -> WithTimeout<Self> {
89        WithTimeout::new_at(self, instant)
90    }
91
92    /// Run two futures in parallel and yield the value of the first to return success. If both
93    /// futures fail, return both errors.
94    fn first_ok2<F>(self, other: F) -> FirstOk2<Self, F>
95    where
96        F: Future<Item = Self::Item>
97    {
98        FirstOk2::new(self, other)
99    }
100
101    /// Resolves `self` while driving `other` in parallel.
102    fn while_driving<F: Future>(self, other: F) -> WhileDriving<Self, F> {
103        WhileDriving::new(self, other)
104    }
105
106    /// Propogates the result of a `.catch_unwind`, panicking if the future resolves to an `Err`
107    fn resume_unwind(self) -> ResumeUnwind<Self>
108    where
109        Self: Future<Error=Box<Any + Send + 'static>>,
110    {
111        ResumeUnwind::new(self)
112    }
113}
114
115impl<T: Future + Sized> FutureExt for T {}
116