composable_utils/lib.rs
1#![forbid(unsafe_code)]
2#![warn(clippy::pedantic)]
3
4use std::future::Future;
5
6#[allow(async_fn_in_trait)]
7pub trait AsyncOptionExt<T> {
8 /// Maps an `Option<T>` to `Option<U>` by applying a function to a contained value (if `Some`) or returns `None` (if `None`).
9 ///
10 /// # Example
11 ///
12 /// ```rust
13 /// use composable_utils::AsyncOptionExt;
14 ///
15 /// async fn double(x: usize) -> usize {
16 /// x * 2
17 /// }
18 ///
19 /// async_io::block_on(async {
20 /// let value = Some(69);
21 /// let value = value
22 /// .async_map(|v| async move { double(v).await })
23 /// .await
24 /// .unwrap_or_else(|| panic!("value should always be Some"));
25 /// assert_eq!(value, 138);
26 /// });
27 /// ```
28 async fn async_map<U, Fut: Future<Output = U>, F: FnOnce(T) -> Fut>(self, f: F) -> Option<U>;
29}
30
31pub trait ResultOptionExt<T, E> {
32 /// Maps either a `Result<Option<T>, E>` or `Option<Result<T, E>>` to a `Result<T, E2>` where `E2` is the type of the provided `err`.
33 ///
34 /// If it's a Result:
35 /// - Ok(Some(t)) -> Ok(t)
36 /// - Ok(None) -> Err(err)
37 /// - Err(_) -> Err(err)
38 ///
39 /// If it's an Option:
40 /// - Some(t) -> Ok(t)
41 /// - None -> Err(err)
42 ///
43 /// # Example
44 ///
45 /// ```rust
46 /// use composable_utils::ResultOptionExt;
47 ///
48 /// enum ErrorOne {
49 /// One,
50 /// }
51 ///
52 /// enum ErrorTwo {
53 /// Two,
54 /// }
55 ///
56 /// fn result_ok_none() -> Result<Option<&'static str>, ErrorOne> {
57 /// Ok(None)
58 /// }
59 ///
60 /// fn option_some_err() -> Option<Result<&'static str, ErrorOne>> {
61 /// Some(Err(ErrorOne::One))
62 /// }
63 ///
64 /// assert!(matches!(result_ok_none().unwrap_or_err(ErrorTwo::Two), Err(ErrorTwo::Two)));
65 /// assert!(matches!(option_some_err().unwrap_or_err(ErrorTwo::Two), Err(ErrorTwo::Two)));
66 /// ```
67 fn unwrap_or_err<E2>(self, err: E2) -> Result<T, E2>;
68
69 /// Maps either a `Result<Option<T>, E>` or `Option<Result<T, E>>` to a `Result<T, E2>` where `E2` is the type of the result of the provided closure.
70 ///
71 /// If it's a Result:
72 /// - Ok(Some(t)) -> Ok(t)
73 /// - Ok(None) -> Err(f())
74 /// - Err(_) -> Err(f())
75 ///
76 /// If it's an Option:
77 /// - Some(t) -> Ok(t)
78 /// - None -> Err(f())
79 ///
80 /// # Example
81 ///
82 /// ```rust
83 /// use composable_utils::ResultOptionExt;
84 ///
85 /// enum ErrorOne {
86 /// One,
87 /// }
88 ///
89 /// enum ErrorTwo {
90 /// Two,
91 /// }
92 ///
93 /// fn result_ok_none() -> Result<Option<&'static str>, ErrorOne> {
94 /// Ok(None)
95 /// }
96 ///
97 /// fn option_some_err() -> Option<Result<&'static str, ErrorOne>> {
98 /// Some(Err(ErrorOne::One))
99 /// }
100 ///
101 /// assert!(matches!(result_ok_none().unwrap_or_else_err(|| ErrorTwo::Two), Err(ErrorTwo::Two)));
102 /// assert!(matches!(option_some_err().unwrap_or_else_err(|| ErrorTwo::Two), Err(ErrorTwo::Two)));
103 /// ```
104 fn unwrap_or_else_err<E2, F: FnOnce() -> E2>(self, f: F) -> Result<T, E2>;
105
106 /// Maps either a `Result<Option<T>, E>` or `Option<Result<T, E>>` to a `Result<T, E2>` by applying a function to a contained Err value, leaving an Ok value untouched.
107 /// Defaults to `Err(default)` if `None`.
108 ///
109 /// # Example
110 ///
111 /// ```rust
112 /// use composable_utils::ResultOptionExt;
113 ///
114 /// enum ErrorOne {
115 /// One,
116 /// }
117 ///
118 /// enum ErrorTwo {
119 /// Two,
120 /// Three,
121 /// }
122 ///
123 /// fn result_ok_none() -> Result<Option<&'static str>, ErrorOne> {
124 /// Ok(None)
125 /// }
126 ///
127 /// fn option_some_err() -> Option<Result<&'static str, ErrorOne>> {
128 /// Some(Err(ErrorOne::One))
129 /// }
130 ///
131 /// assert!(matches!(result_ok_none().unwrap_or_map_err(ErrorTwo::Two, |err| ErrorTwo::Three), Err(ErrorTwo::Two)));
132 /// assert!(matches!(option_some_err().unwrap_or_map_err(ErrorTwo::Two, |err| ErrorTwo::Three), Err(ErrorTwo::Three)));
133 /// ```
134 fn unwrap_or_map_err<E2, F: FnOnce(E) -> E2>(self, default: E2, f: F) -> Result<T, E2>;
135
136 /// Maps either a `Result<Option<T>, E>` or `Option<Result<T, E>>` to a `Result<T, E2>` by applying a function to a contained Err value, leaving an Ok value untouched.
137 /// Defaults to `Err(default)` if `None`.
138 ///
139 /// # Example
140 ///
141 /// ```rust
142 /// use composable_utils::ResultOptionExt;
143 ///
144 /// enum ErrorOne {
145 /// One,
146 /// }
147 ///
148 /// enum ErrorTwo {
149 /// Two,
150 /// Three,
151 /// }
152 ///
153 /// fn result_ok_none() -> Result<Option<&'static str>, ErrorOne> {
154 /// Ok(None)
155 /// }
156 ///
157 /// fn option_some_err() -> Option<Result<&'static str, ErrorOne>> {
158 /// Some(Err(ErrorOne::One))
159 /// }
160 ///
161 /// assert!(matches!(result_ok_none().unwrap_or_else_map_err(|| ErrorTwo::Two, |err| ErrorTwo::Three), Err(ErrorTwo::Two)));
162 /// assert!(matches!(option_some_err().unwrap_or_else_map_err(|| ErrorTwo::Two, |err| ErrorTwo::Three), Err(ErrorTwo::Three)));
163 /// ```
164 fn unwrap_or_else_map_err<E2, F: FnOnce(E) -> E2, F2: FnOnce() -> E2>(self, default: F2, f: F) -> Result<T, E2>;
165}
166
167impl<T> AsyncOptionExt<T> for Option<T> {
168 async fn async_map<U, Fut: Future<Output = U>, F: FnOnce(T) -> Fut>(self, f: F) -> Option<U> {
169 match self {
170 Some(t) => Some(f(t).await),
171 None => None,
172 }
173 }
174}
175
176impl<T, E> ResultOptionExt<T, E> for Option<Result<T, E>> {
177 fn unwrap_or_err<E2>(self, err: E2) -> Result<T, E2> {
178 match self {
179 Some(Ok(t)) => Ok(t),
180 Some(Err(_)) => Err(err),
181 None => Err(err),
182 }
183 }
184
185 fn unwrap_or_else_err<E2, F: FnOnce() -> E2>(self, f: F) -> Result<T, E2> {
186 match self {
187 Some(Ok(t)) => Ok(t),
188 Some(Err(_)) => Err(f()),
189 None => Err(f()),
190 }
191 }
192
193 fn unwrap_or_map_err<E2, F: FnOnce(E) -> E2>(self, default: E2, f: F) -> Result<T, E2> {
194 match self {
195 Some(Ok(t)) => Ok(t),
196 Some(Err(e)) => Err(f(e)),
197 None => Err(default),
198 }
199 }
200
201 fn unwrap_or_else_map_err<E2, F: FnOnce(E) -> E2, F2: FnOnce() -> E2>(self, default: F2, f: F) -> Result<T, E2> {
202 match self {
203 Some(Ok(t)) => Ok(t),
204 Some(Err(e)) => Err(f(e)),
205 None => Err(default()),
206 }
207 }
208}
209
210impl<T, E> ResultOptionExt<T, E> for Result<Option<T>, E> {
211 fn unwrap_or_err<E2>(self, err: E2) -> Result<T, E2> {
212 match self {
213 Ok(t) => match t {
214 Some(t) => Ok(t),
215 None => Err(err),
216 },
217 Err(_) => Err(err),
218 }
219 }
220
221 fn unwrap_or_else_err<E2, F: FnOnce() -> E2>(self, f: F) -> Result<T, E2> {
222 match self {
223 Ok(t) => match t {
224 Some(t) => Ok(t),
225 None => Err(f()),
226 },
227 Err(_) => Err(f()),
228 }
229 }
230
231 fn unwrap_or_map_err<E2, F: FnOnce(E) -> E2>(self, default: E2, f: F) -> Result<T, E2> {
232 match self {
233 Ok(t) => match t {
234 Some(t) => Ok(t),
235 None => Err(default),
236 },
237 Err(e) => Err(f(e)),
238 }
239 }
240
241 fn unwrap_or_else_map_err<E2, F: FnOnce(E) -> E2, F2: FnOnce() -> E2>(self, default: F2, f: F) -> Result<T, E2> {
242 match self {
243 Ok(t) => match t {
244 Some(t) => Ok(t),
245 None => Err(default()),
246 },
247 Err(e) => Err(f(e)),
248 }
249 }
250}