1use anyhow::{Context as AnyhowContext, Result};
2use std::fmt::Display;
3
4pub trait ResultExt<T> {
6 fn and_then_async<F, U>(self, f: F) -> Result<U>
8 where
9 F: FnOnce(T) -> Result<U>;
10
11 fn or_else_with<F>(self, f: F) -> Result<T>
13 where
14 F: FnOnce() -> Result<T>;
15
16 fn map_err_context<C>(self, context: C) -> Result<T>
18 where
19 C: Display + Send + Sync + 'static;
20
21 fn map_ok<F, U>(self, f: F) -> Result<U>
23 where
24 F: FnOnce(T) -> U;
25
26 fn tap<F>(self, f: F) -> Result<T>
28 where
29 F: FnOnce(&T);
30
31 fn tap_err<F>(self, f: F) -> Result<T>
33 where
34 F: FnOnce(&anyhow::Error);
35}
36
37impl<T> ResultExt<T> for Result<T> {
38 fn and_then_async<F, U>(self, f: F) -> Result<U>
39 where
40 F: FnOnce(T) -> Result<U>,
41 {
42 self.and_then(f)
43 }
44
45 fn or_else_with<F>(self, f: F) -> Result<T>
46 where
47 F: FnOnce() -> Result<T>,
48 {
49 self.or_else(|_| f())
50 }
51
52 fn map_err_context<C>(self, context: C) -> Result<T>
53 where
54 C: Display + Send + Sync + 'static,
55 {
56 self.context(context)
57 }
58
59 fn map_ok<F, U>(self, f: F) -> Result<U>
60 where
61 F: FnOnce(T) -> U,
62 {
63 self.map(f)
64 }
65
66 fn tap<F>(self, f: F) -> Result<T>
67 where
68 F: FnOnce(&T),
69 {
70 if let Ok(ref value) = self {
71 f(value);
72 }
73 self
74 }
75
76 fn tap_err<F>(self, f: F) -> Result<T>
77 where
78 F: FnOnce(&anyhow::Error),
79 {
80 if let Err(ref e) = self {
81 f(e);
82 }
83 self
84 }
85}
86
87pub trait OptionExt<T> {
89 fn ok_or_error<E>(self, error: E) -> Result<T>
91 where
92 E: Into<anyhow::Error>;
93
94 fn and_then_some<F, U>(self, f: F) -> Option<U>
96 where
97 F: FnOnce(T) -> Option<U>;
98
99 fn or_else_some<F>(self, f: F) -> Option<T>
101 where
102 F: FnOnce() -> Option<T>;
103
104 fn filter_some<F>(self, predicate: F) -> Option<T>
106 where
107 F: FnOnce(&T) -> bool;
108}
109
110impl<T> OptionExt<T> for Option<T> {
111 fn ok_or_error<E>(self, error: E) -> Result<T>
112 where
113 E: Into<anyhow::Error>,
114 {
115 self.ok_or_else(|| error.into())
116 }
117
118 fn and_then_some<F, U>(self, f: F) -> Option<U>
119 where
120 F: FnOnce(T) -> Option<U>,
121 {
122 self.and_then(f)
123 }
124
125 fn or_else_some<F>(self, f: F) -> Option<T>
126 where
127 F: FnOnce() -> Option<T>,
128 {
129 self.or_else(f)
130 }
131
132 fn filter_some<F>(self, predicate: F) -> Option<T>
133 where
134 F: FnOnce(&T) -> bool,
135 {
136 self.filter(predicate)
137 }
138}
139
140pub struct Applicative<T> {
142 values: Vec<T>,
143}
144
145impl<T> Applicative<T> {
146 pub fn new(values: Vec<T>) -> Self {
148 Self { values }
149 }
150
151 pub fn apply<F, U>(self, f: F) -> Applicative<U>
153 where
154 F: Fn(T) -> U,
155 {
156 Applicative::new(self.values.into_iter().map(f).collect())
157 }
158
159 pub fn apply_result<F, U>(self, f: F) -> Result<Applicative<U>>
161 where
162 F: Fn(T) -> Result<U>,
163 {
164 let results: Result<Vec<U>> = self.values.into_iter().map(f).collect();
165 results.map(Applicative::new)
166 }
167
168 pub fn unwrap(self) -> Vec<T> {
170 self.values
171 }
172}
173
174pub fn compose_results<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> Result<C>
176where
177 F: Fn(A) -> Result<B>,
178 G: Fn(B) -> Result<C>,
179{
180 move |a| f(a).and_then(&g)
181}
182
183pub fn lift_result<T, U, F>(f: F) -> impl Fn(T) -> Result<U>
185where
186 F: Fn(T) -> U,
187{
188 move |t| Ok(f(t))
189}
190
191pub fn sequence_results<T>(results: Vec<Result<T>>) -> Result<Vec<T>> {
193 results.into_iter().collect()
194}
195
196pub fn traverse_results<T, U, F>(values: Vec<T>, f: F) -> Result<Vec<U>>
198where
199 F: Fn(T) -> Result<U>,
200{
201 values.into_iter().map(f).collect()
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use anyhow::anyhow;
208
209 #[test]
210 fn test_result_ext() {
211 let result: Result<i32> = Ok(42);
212
213 let chained = result
214 .map_ok(|x| x * 2)
215 .and_then_async(|x| Ok(x + 1))
216 .tap(|x| println!("Value: {x}"));
217
218 assert_eq!(chained.unwrap(), 85);
219 }
220
221 #[test]
222 fn test_option_ext() {
223 let value = Some(42);
224
225 let result = value
226 .and_then_some(|x| Some(x * 2))
227 .filter_some(|&x| x > 50)
228 .ok_or_error(anyhow!("Value too small"));
229
230 assert!(result.is_ok());
231 assert_eq!(result.unwrap(), 84);
232 }
233
234 #[test]
235 fn test_kleisli_composition() {
236 let add_one = |x: i32| -> Result<i32> { Ok(x + 1) };
237 let double = |x: i32| -> Result<i32> { Ok(x * 2) };
238
239 let composed = compose_results(add_one, double);
240 assert_eq!(composed(5).unwrap(), 12); }
242
243 #[test]
244 fn test_sequence() {
245 let results = vec![Ok(1), Ok(2), Ok(3)];
246 let sequenced = sequence_results(results);
247 assert_eq!(sequenced.unwrap(), vec![1, 2, 3]);
248
249 let with_error = vec![Ok(1), Err(anyhow!("error")), Ok(3)];
250 let sequenced_err = sequence_results(with_error);
251 assert!(sequenced_err.is_err());
252 }
253}