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}