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
105impl<T, E> AsyncResultExt<T, E> for Result<T, E> {
106    async fn async_map<U, F, Fut>(self, op: F) -> Result<U, E>
107    where
108        F: FnOnce(T) -> Fut,
109        Fut: Future<Output = U>,
110    {
111        match self {
112            Ok(value) => Ok(op(value).await),
113            Err(err) => Err(err),
114        }
115    }
116
117    async fn async_and_then<U, F, Fut>(self, op: F) -> Result<U, E>
118    where
119        F: FnOnce(T) -> Fut,
120        Fut: Future<Output = Result<U, E>>,
121    {
122        match self {
123            Ok(value) => op(value).await,
124            Err(err) => Err(err),
125        }
126    }
127
128    async fn async_map_or<U, F, Fut>(self, default: U, op: F) -> U
129    where
130        F: FnOnce(T) -> Fut,
131        Fut: Future<Output = U>,
132    {
133        match self {
134            Ok(value) => op(value).await,
135            Err(_) => default,
136        }
137    }
138    async fn async_map_or_else<U, D, F, Fut, DefFut>(self, default: D, op: F) -> U
139    where
140        D: FnOnce(E) -> DefFut,
141        F: FnOnce(T) -> Fut,
142        DefFut: Future<Output = U>,
143        Fut: Future<Output = U>,
144    {
145        match self {
146            Ok(value) => op(value).await,
147            Err(err) => default(err).await,
148        }
149    }
150
151    async fn async_map_err<F, Fut, O>(self, op: F) -> Result<T, O>
152    where
153        F: FnOnce(E) -> Fut,
154        Fut: Future<Output = O>,
155    {
156        match self {
157            Ok(value) => Ok(value),
158            Err(err) => Err(op(err).await),
159        }
160    }
161
162    async fn async_inspect<F, Fut>(self, op: F) -> Self
163    where
164        F: FnOnce(&T) -> Fut,
165        Fut: Future<Output = ()>,
166    {
167        if let Ok(ref value) = self {
168            op(value).await;
169        }
170        self
171    }
172
173    async fn async_inspect_err<F, Fut>(self, op: F) -> Self
174    where
175        F: FnOnce(&E) -> Fut,
176        Fut: Future<Output = ()>,
177    {
178        if let Err(ref err) = self {
179            op(err).await;
180        }
181        self
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[tokio::test]
190    async fn test_async_map() {
191        let r: Result<i32, &str> = Ok(2);
192        let res = r.async_map(|v| async move { v * 3 }).await;
193        assert_eq!(res, Ok(6));
194
195        let r: Result<i32, &str> = Err("error");
196        let res = r.async_map(|v| async move { v * 3 }).await;
197        assert_eq!(res, Err("error"));
198    }
199
200    #[tokio::test]
201    async fn test_async_and_then() {
202        let r: Result<i32, &str> = Ok(2);
203        let res = r.async_and_then(|v| async move { Ok(v * 5) }).await;
204        assert_eq!(res, Ok(10));
205
206        let r: Result<i32, &str> = Err("fail");
207        let res = r.async_and_then(|v| async move { Ok(v * 5) }).await;
208        assert_eq!(res, Err("fail"));
209    }
210
211    #[tokio::test]
212    async fn test_async_map_or() {
213        let r: Result<i32, &str> = Ok(2);
214        let res = r.async_map_or(100, |v| async move { v * 4 }).await;
215        assert_eq!(res, 8);
216
217        let r: Result<i32, &str> = Err("fail");
218        let res = r.async_map_or(100, |v| async move { v * 4 }).await;
219        assert_eq!(res, 100);
220    }
221
222    #[tokio::test]
223    async fn test_async_map_err() {
224        let r: Result<i32, &str> = Ok(10);
225        let res = r.async_map_err(|e| async move { e.len() }).await;
226        assert_eq!(res, Ok(10));
227
228        let r: Result<i32, &str> = Err("fail");
229        let res = r.async_map_err(|e| async move { e.len() }).await;
230        assert_eq!(res, Err(4));
231    }
232
233    #[tokio::test]
234    async fn test_async_inspect_err() {
235        let r: Result<i32, &str> = Err("oops");
236        let mut seen = "";
237        let res = r
238            .async_inspect_err(|e| async {
239                seen = e;
240            })
241            .await;
242        assert_eq!(res, Err("oops"));
243        assert_eq!(seen, "oops");
244
245        let r: Result<i32, &str> = Ok(10);
246        let res = r
247            .async_inspect_err(|e| async {
248                seen = e;
249            })
250            .await;
251        assert_eq!(res, Ok(10));
252    }
253
254    #[tokio::test]
255    async fn test_async_map_or_else() {
256        let r: Result<i32, &str> = Ok(3);
257        let res = r
258            .async_map_or_else(|e| async move { e.len() as i32 }, |v| async move { v * 2 })
259            .await;
260        assert_eq!(res, 6);
261
262        let r: Result<i32, &str> = Err("error");
263        let res = r
264            .async_map_or_else(|e| async move { e.len() as i32 }, |v| async move { v * 2 })
265            .await;
266        assert_eq!(res, 5);
267    }
268}