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}