async_result_ext/
lib.rs

1use std::future::Future;
2
3/// Asynchronous extensions for [`Result<T, E>`].
4///
5/// This trait provides async counterparts of common `Result` methods
6/// (`map`, `and_then`, `map_err`, `inspect`) that accept asynchronous closures.
7///
8/// Example:
9/// ```
10/// use async_result_ext::AsyncResultExt;
11///
12/// #[tokio::main]
13/// async fn main() {
14///     let r: Result<i32, &str> = Ok(2);
15///
16///     let doubled = r.async_map(|v| async move { v * 2 }).await;
17///     assert_eq!(doubled, Ok(4));
18/// }
19/// ```
20pub trait AsyncResultExt<T, E> {
21    /// Asynchronous version of [`Result::map`].
22    ///
23    /// Applies an async function `op` to the `Ok` value.
24    /// If the result is `Err`, it is returned unchanged.
25    ///
26    /// ```
27    /// use async_result_ext::AsyncResultExt;
28    ///
29    /// # #[tokio::main]
30    /// # async fn main() {
31    /// let r: Result<i32, &str> = Ok(5);
32    /// let res = r.async_map(|v| async move { v + 1 }).await;
33    /// assert_eq!(res, Ok(6));
34    /// # }
35    /// ```
36    fn async_map<U, F, Fut>(self, op: F) -> impl Future<Output = Result<U, E>>
37    where
38        F: FnOnce(T) -> Fut,
39        Fut: Future<Output = U>;
40
41    /// Asynchronous version of [`Result::and_then`].
42    ///
43    /// Chains async computations that return `Result`.
44    ///
45    /// ```
46    /// use async_result_ext::AsyncResultExt;
47    ///
48    /// # #[tokio::main]
49    /// # async fn main() {
50    /// let r: Result<i32, &str> = Ok(2);
51    /// let res = r.async_and_then(|v| async move { Ok(v * 3) }).await;
52    /// assert_eq!(res, Ok(6));
53    /// # }
54    /// ```
55    fn async_and_then<U, F, Fut>(self, op: F) -> impl Future<Output = Result<U, E>>
56    where
57        F: FnOnce(T) -> Fut,
58        Fut: Future<Output = Result<U, E>>;
59
60    /// Asynchronous version of [`Result::map_or`].
61    ///
62    /// If the result is `Ok`, applies async function `op`.
63    /// If `Err`, returns the provided `default` value.
64    fn async_map_or<U, F, Fut>(self, default: U, op: F) -> impl Future<Output = U>
65    where
66        F: FnOnce(T) -> Fut,
67        Fut: Future<Output = U>;
68
69    /// Asynchronous version of [`Result::map_or_else`].
70    ///
71    /// If the result is `Ok`, applies async function `op`.
72    /// If `Err`, computes an async fallback via `default`.
73    fn async_map_or_else<U, D, F, Fut, DefFut>(self, default: D, op: F) -> impl Future<Output = U>
74    where
75        D: FnOnce(E) -> DefFut,
76        F: FnOnce(T) -> Fut,
77        DefFut: Future<Output = U>,
78        Fut: Future<Output = U>;
79
80    /// Asynchronous version of [`Result::map_err`].
81    ///
82    /// Transforms the error using an async function `op`.
83    fn async_map_err<F, Fut, O>(self, op: F) -> impl Future<Output = Result<T, O>>
84    where
85        F: FnOnce(E) -> Fut,
86        Fut: Future<Output = O>;
87
88    /// Asynchronous version of [`Result::inspect`].
89    ///
90    /// Lets you asynchronously “peek” into the `Ok` value without modifying it.
91    fn async_inspect<F, Fut>(self, op: F) -> impl Future<Output = Self>
92    where
93        F: FnOnce(&T) -> Fut,
94        Fut: Future<Output = ()>;
95
96    /// Asynchronous version of [`Result::inspect_err`].
97    ///
98    /// Lets you asynchronously “peek” into the `Err` value without modifying it.
99    fn async_inspect_err<F, Fut>(self, op: F) -> impl Future<Output = Self>
100    where
101        F: FnOnce(&E) -> Fut,
102        Fut: Future<Output = ()>;
103
104    /// Asynchronous version of [`Result::is_ok_and`].
105    ///
106    /// Returns `true` if the result is `Ok` **and** the async predicate returns `true`.
107    /// Returns `false` if the result is `Err` or the predicate resolves to `false`.
108    ///
109    /// ```
110    /// use async_result_ext::AsyncResultExt;
111    ///
112    /// # #[tokio::main]
113    /// # async fn main() {
114    /// let r: Result<i32, &str> = Ok(10);
115    /// let is_even = r.async_is_ok_and(|v| async move { v % 2 == 0 }).await;
116    /// assert!(is_even);
117    ///
118    /// let r: Result<i32, &str> = Ok(3);
119    /// let is_even = r.async_is_ok_and(|v| async move { v % 2 == 0 }).await;
120    /// assert!(!is_even);
121    ///
122    /// let r: Result<i32, &str> = Err("error");
123    /// let is_even = r.async_is_ok_and(|v| async move { v % 2 == 0 }).await;
124    /// assert!(!is_even);
125    /// # }
126    /// ```
127    fn async_is_ok_and<F, Fut>(self, op: F) -> impl Future<Output = bool>
128    where
129        F: FnOnce(T) -> Fut,
130        Fut: Future<Output = bool>;
131
132    /// Asynchronous version of [`Result::is_err_and`].
133    ///
134    /// Returns `true` if the result is `Err` **and** the async predicate returns `true`.
135    /// Returns `false` if the result is `Ok` or the predicate resolves to `false`.
136    ///
137    /// ```
138    /// use async_result_ext::AsyncResultExt;
139    ///
140    /// # #[tokio::main]
141    /// # async fn main() {
142    /// let r: Result<i32, &str> = Err("oops");
143    /// let is_long = r.async_is_err_and(|e| async move { e.len() > 3 }).await;
144    /// assert!(is_long);
145    ///
146    /// let r: Result<i32, &str> = Err("no");
147    /// let is_long = r.async_is_err_and(|e| async move { e.len() > 3 }).await;
148    /// assert!(!is_long);
149    ///
150    /// let r: Result<i32, &str> = Ok(42);
151    /// let is_long = r.async_is_err_and(|e| async move { e.len() > 3 }).await;
152    /// assert!(!is_long);
153    /// # }
154    /// ```
155    fn async_is_err_and<F, Fut>(self, op: F) -> impl Future<Output = bool>
156    where
157        F: FnOnce(E) -> Fut,
158        Fut: Future<Output = bool>;
159}
160
161impl<T, E> AsyncResultExt<T, E> for Result<T, E> {
162    async fn async_map<U, F, Fut>(self, op: F) -> Result<U, E>
163    where
164        F: FnOnce(T) -> Fut,
165        Fut: Future<Output = U>,
166    {
167        match self {
168            Ok(value) => Ok(op(value).await),
169            Err(err) => Err(err),
170        }
171    }
172
173    async fn async_and_then<U, F, Fut>(self, op: F) -> Result<U, E>
174    where
175        F: FnOnce(T) -> Fut,
176        Fut: Future<Output = Result<U, E>>,
177    {
178        match self {
179            Ok(value) => op(value).await,
180            Err(err) => Err(err),
181        }
182    }
183
184    async fn async_map_or<U, F, Fut>(self, default: U, op: F) -> U
185    where
186        F: FnOnce(T) -> Fut,
187        Fut: Future<Output = U>,
188    {
189        match self {
190            Ok(value) => op(value).await,
191            Err(_) => default,
192        }
193    }
194    async fn async_map_or_else<U, D, F, Fut, DefFut>(self, default: D, op: F) -> U
195    where
196        D: FnOnce(E) -> DefFut,
197        F: FnOnce(T) -> Fut,
198        DefFut: Future<Output = U>,
199        Fut: Future<Output = U>,
200    {
201        match self {
202            Ok(value) => op(value).await,
203            Err(err) => default(err).await,
204        }
205    }
206
207    async fn async_map_err<F, Fut, O>(self, op: F) -> Result<T, O>
208    where
209        F: FnOnce(E) -> Fut,
210        Fut: Future<Output = O>,
211    {
212        match self {
213            Ok(value) => Ok(value),
214            Err(err) => Err(op(err).await),
215        }
216    }
217
218    async fn async_inspect<F, Fut>(self, op: F) -> Self
219    where
220        F: FnOnce(&T) -> Fut,
221        Fut: Future<Output = ()>,
222    {
223        if let Ok(ref value) = self {
224            op(value).await;
225        }
226        self
227    }
228
229    async fn async_inspect_err<F, Fut>(self, op: F) -> Self
230    where
231        F: FnOnce(&E) -> Fut,
232        Fut: Future<Output = ()>,
233    {
234        if let Err(ref err) = self {
235            op(err).await;
236        }
237        self
238    }
239
240    async fn async_is_ok_and<F, Fut>(self, op: F) -> bool
241    where
242        F: FnOnce(T) -> Fut,
243        Fut: Future<Output = bool>,
244    {
245        {
246            match self {
247                Err(_) => false,
248                Ok(v) => op(v).await,
249            }
250        }
251    }
252
253    async fn async_is_err_and<F, Fut>(self, op: F) -> bool
254    where
255        F: FnOnce(E) -> Fut,
256        Fut: Future<Output = bool>,
257    {
258        {
259            match self {
260                Ok(_) => false,
261                Err(e) => op(e).await,
262            }
263        }
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270
271    #[tokio::test]
272    async fn test_async_map() {
273        let r: Result<i32, &str> = Ok(2);
274        let res = r.async_map(|v| async move { v * 3 }).await;
275        assert_eq!(res, Ok(6));
276
277        let r: Result<i32, &str> = Err("error");
278        let res = r.async_map(|v| async move { v * 3 }).await;
279        assert_eq!(res, Err("error"));
280    }
281
282    #[tokio::test]
283    async fn test_async_and_then() {
284        let r: Result<i32, &str> = Ok(2);
285        let res = r.async_and_then(|v| async move { Ok(v * 5) }).await;
286        assert_eq!(res, Ok(10));
287
288        let r: Result<i32, &str> = Err("fail");
289        let res = r.async_and_then(|v| async move { Ok(v * 5) }).await;
290        assert_eq!(res, Err("fail"));
291    }
292
293    #[tokio::test]
294    async fn test_async_map_or() {
295        let r: Result<i32, &str> = Ok(2);
296        let res = r.async_map_or(100, |v| async move { v * 4 }).await;
297        assert_eq!(res, 8);
298
299        let r: Result<i32, &str> = Err("fail");
300        let res = r.async_map_or(100, |v| async move { v * 4 }).await;
301        assert_eq!(res, 100);
302    }
303
304    #[tokio::test]
305    async fn test_async_map_err() {
306        let r: Result<i32, &str> = Ok(10);
307        let res = r.async_map_err(|e| async move { e.len() }).await;
308        assert_eq!(res, Ok(10));
309
310        let r: Result<i32, &str> = Err("fail");
311        let res = r.async_map_err(|e| async move { e.len() }).await;
312        assert_eq!(res, Err(4));
313    }
314
315    #[tokio::test]
316    async fn test_async_inspect_err() {
317        let r: Result<i32, &str> = Err("oops");
318        let mut seen = "";
319        let res = r
320            .async_inspect_err(|e| async {
321                seen = e;
322            })
323            .await;
324        assert_eq!(res, Err("oops"));
325        assert_eq!(seen, "oops");
326
327        let r: Result<i32, &str> = Ok(10);
328        let res = r
329            .async_inspect_err(|e| async {
330                seen = e;
331            })
332            .await;
333        assert_eq!(res, Ok(10));
334    }
335
336    #[tokio::test]
337    async fn test_async_map_or_else() {
338        let r: Result<i32, &str> = Ok(3);
339        let res = r
340            .async_map_or_else(|e| async move { e.len() as i32 }, |v| async move { v * 2 })
341            .await;
342        assert_eq!(res, 6);
343
344        let r: Result<i32, &str> = Err("error");
345        let res = r
346            .async_map_or_else(|e| async move { e.len() as i32 }, |v| async move { v * 2 })
347            .await;
348        assert_eq!(res, 5);
349    }
350
351    #[tokio::test]
352    async fn async_is_ok_and() {
353        let r: Result<i32, &str> = Ok(5);
354        let res = r.async_is_ok_and(|_| async move { false }).await;
355        assert!(!res);
356
357        let r: Result<i32, &str> = Err("error");
358        let res = r.async_is_ok_and(|_| async move { false }).await;
359        assert!(!res);
360    }
361
362    #[tokio::test]
363    async fn is_err_and() {
364        let r: Result<i32, &str> = Ok(5);
365        let res = r.async_is_err_and(|_| async move { true }).await;
366        assert!(!res);
367
368        let r: Result<i32, &str> = Err("error");
369        let res = r.async_is_err_and(|_| async move { false }).await;
370        assert!(!res);
371    }
372}