Skip to main content

async_iter_ext/
option.rs

1/// Asynchronous extension methods for `Option<T>`.
2///
3/// This trait allows using asynchronous functions with `Option` types,
4pub trait AsyncOptionTools<T> {
5    /// Asynchronously checks if the option is `Some` and satisfies a predicate.
6    ///
7    /// Returns `false` if the value is `None`. If `Some`, the provided async function is run.
8    ///
9    /// # Examples
10    ///
11    /// ```
12    /// use async_std::task;
13    /// use async_iter_ext::AsyncOptionTools;
14    ///
15    /// task::block_on(async {
16    ///     async fn is_positive(x: i32) -> bool {
17    ///         x > 0
18    ///     }
19    ///
20    ///     let some = Some(10);
21    ///     let none: Option<i32> = None;
22    ///
23    ///     assert_eq!(some.is_some_and_async(is_positive).await, true);
24    ///     assert_eq!(none.is_some_and_async(is_positive).await, false);
25    /// });
26    /// ```
27    #[allow(clippy::wrong_self_convention)]
28    fn is_some_and_async<F, Fut>(self, f: F) -> impl Future<Output = bool>
29    where
30        F: FnOnce(T) -> Fut,
31        Fut: Future<Output = bool>;
32
33    /// Asynchronously checks if the option is `None` or satisfies a predicate.
34    ///
35    /// - Returns `true` if the value is `None`.
36    /// - If `Some`, runs the async predicate and returns its result.
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use async_std::task;
42    /// use async_iter_ext::AsyncOptionTools;
43    ///
44    /// task::block_on(async {
45    ///     async fn is_zero(x: u8) -> bool {
46    ///         x == 0
47    ///     }
48    ///
49    ///     let some = Some(0);
50    ///     let none: Option<u8> = None;
51    ///
52    ///     assert_eq!(some.is_none_or_async(is_zero).await, true);
53    ///     assert_eq!(
54    ///         some.is_none_or_async(|x| async move { x > 1 }).await,
55    ///         false
56    ///     );
57    ///     assert_eq!(none.is_none_or_async(is_zero).await, true);
58    /// });
59    /// ```
60    #[allow(clippy::wrong_self_convention)]
61    fn is_none_or_async<F, Fut>(self, f: F) -> impl Future<Output = bool>
62    where
63        F: FnOnce(T) -> Fut,
64        Fut: Future<Output = bool>;
65
66    /// Asynchronously maps an `Option<T>` to an `Option<B>` using an async function.
67    ///
68    /// - If `Some`, the function is awaited and wrapped in `Some`.
69    /// - If `None`, returns `None`.
70    ///
71    /// # Examples
72    ///
73    /// ```
74    /// use async_std::task;
75    /// use async_iter_ext::AsyncOptionTools;
76    ///
77    /// task::block_on(async {
78    ///     async fn to_string_async(n: i32) -> String {
79    ///         format!("Number: {}", n)
80    ///     }
81    ///
82    ///     let some = Some(42);
83    ///     let result = some.map_async(to_string_async).await;
84    ///     assert_eq!(result, Some("Number: 42".to_string()));
85    ///
86    ///     let none: Option<i32> = None;
87    ///     let result = none.map_async(to_string_async).await;
88    ///     assert_eq!(result, None);
89    /// });
90    /// ```
91    #[allow(clippy::wrong_self_convention)]
92    fn map_async<B, F, Fut>(self, f: F) -> impl Future<Output = Option<B>>
93    where
94        F: FnOnce(T) -> Fut,
95        Fut: Future<Output = B>;
96}
97
98impl<T> AsyncOptionTools<T> for Option<T> {
99    async fn is_some_and_async<F, Fut>(self, f: F) -> bool
100    where
101        F: FnOnce(T) -> Fut,
102        Fut: Future<Output = bool>,
103    {
104        if let Some(x) = self {
105            f(x).await
106        } else {
107            false
108        }
109    }
110
111    async fn is_none_or_async<F, Fut>(self, f: F) -> bool
112    where
113        F: FnOnce(T) -> Fut,
114        Fut: Future<Output = bool>,
115    {
116        if let Some(x) = self { f(x).await } else { true }
117    }
118
119    async fn map_async<B, F, Fut>(self, f: F) -> Option<B>
120    where
121        F: FnOnce(T) -> Fut,
122        Fut: Future<Output = B>,
123    {
124        if let Some(x) = self {
125            Some(f(x).await)
126        } else {
127            None
128        }
129    }
130}