overture_core/
result.rs

1// Result utilities for combining and transforming Result types
2// Equivalent to Swift's Result zip functions
3
4/// Transforms a pair of results into a result of a pair.
5/// Equivalent to Swift's zip<A, B, Error>(_ result1: Result<A, Error>, _ result2: Result<B, Error>) -> Result<(A, B), Error>
6pub fn zip<A, B, E>(result1: Result<A, E>, result2: Result<B, E>) -> Result<(A, B), E> {
7    result1.and_then(|a| result2.map(|b| (a, b)))
8}
9
10/// Transforms a pair of results into a new result.
11/// Equivalent to Swift's zip<A, B, Z, Error>(with transform: @escaping (A, B) -> Z, _ result1: Result<A, Error>, _ result2: Result<B, Error>) -> Result<Z, Error>
12pub fn zip_with<A, B, Z, E, F>(
13    result1: Result<A, E>,
14    result2: Result<B, E>,
15    transform: F,
16) -> Result<Z, E>
17where
18    F: FnOnce(A, B) -> Z,
19{
20    zip(result1, result2).map(|(a, b)| transform(a, b))
21}
22
23/// Transforms three results into a result of a tuple.
24pub fn zip3<A, B, C, E>(
25    result1: Result<A, E>,
26    result2: Result<B, E>,
27    result3: Result<C, E>,
28) -> Result<(A, B, C), E> {
29    result1.and_then(|a| result2.and_then(|b| result3.map(|c| (a, b, c))))
30}
31
32/// Transforms three results into a new result.
33pub fn zip3_with<A, B, C, Z, E, F>(
34    result1: Result<A, E>,
35    result2: Result<B, E>,
36    result3: Result<C, E>,
37    transform: F,
38) -> Result<Z, E>
39where
40    F: FnOnce(A, B, C) -> Z,
41{
42    zip3(result1, result2, result3).map(|(a, b, c)| transform(a, b, c))
43}
44
45/// Transforms four results into a result of a tuple.
46pub fn zip4<A, B, C, D, E>(
47    result1: Result<A, E>,
48    result2: Result<B, E>,
49    result3: Result<C, E>,
50    result4: Result<D, E>,
51) -> Result<(A, B, C, D), E> {
52    result1.and_then(|a| result2.and_then(|b| result3.and_then(|c| result4.map(|d| (a, b, c, d)))))
53}
54
55/// Transforms four results into a new result.
56pub fn zip4_with<A, B, C, D, Z, E, F>(
57    result1: Result<A, E>,
58    result2: Result<B, E>,
59    result3: Result<C, E>,
60    result4: Result<D, E>,
61    transform: F,
62) -> Result<Z, E>
63where
64    F: FnOnce(A, B, C, D) -> Z,
65{
66    zip4(result1, result2, result3, result4).map(|(a, b, c, d)| transform(a, b, c, d))
67}
68
69/// Combines multiple results into a single result containing a vector.
70/// If any result is an error, returns the first error.
71pub fn sequence<A, E>(results: Vec<Result<A, E>>) -> Result<Vec<A>, E> {
72    let mut values = Vec::with_capacity(results.len());
73    for result in results {
74        values.push(result?);
75    }
76    Ok(values)
77}
78
79/// Maps over a result and flattens the result.
80/// Equivalent to Swift's flatMap for Result.
81pub fn flat_map<A, B, E, F>(result: Result<A, E>, transform: F) -> Result<B, E>
82where
83    F: FnOnce(A) -> Result<B, E>,
84{
85    result.and_then(transform)
86}
87
88/// Applies a function to the success value if present, otherwise returns the error.
89/// Equivalent to Swift's map for Result.
90pub fn map<A, B, E, F>(result: Result<A, E>, transform: F) -> Result<B, E>
91where
92    F: FnOnce(A) -> B,
93{
94    result.map(transform)
95}
96
97/// Applies a function to the error value if present, otherwise returns the success value.
98/// Equivalent to Swift's mapError for Result.
99pub fn map_error<A, E, F, G>(result: Result<A, E>, transform: F) -> Result<A, F>
100where
101    F: FnOnce(E) -> F,
102{
103    result.map_err(transform)
104}
105
106/// Returns the success value or a default value if it's an error.
107/// Equivalent to Swift's getOrElse for Result.
108pub fn get_or_else<A, E, F>(result: Result<A, E>, default: F) -> A
109where
110    F: FnOnce() -> A,
111{
112    result.unwrap_or_else(|_| default())
113}
114
115/// Returns the success value or a default value if it's an error.
116pub fn get_or_default<A, E>(result: Result<A, E>) -> A
117where
118    A: Default,
119{
120    result.unwrap_or_default()
121}
122
123/// Converts an Option to a Result.
124pub fn from_option<A, E>(option: Option<A>, error: E) -> Result<A, E> {
125    option.ok_or(error)
126}
127
128/// Converts a Result to an Option, discarding the error.
129pub fn to_option<A, E>(result: Result<A, E>) -> Option<A> {
130    result.ok()
131}
132
133/// Combines two results, returning the first success or the first error.
134pub fn or<A, E>(result1: Result<A, E>, result2: Result<A, E>) -> Result<A, E> {
135    match result1 {
136        Ok(value) => Ok(value),
137        Err(_) => result2,
138    }
139}
140
141/// Combines two results, returning the first success or the first error.
142pub fn or_else<A, E, F>(result: Result<A, E>, fallback: F) -> Result<A, E>
143where
144    F: FnOnce() -> Result<A, E>,
145{
146    match result {
147        Ok(value) => Ok(value),
148        Err(_) => fallback(),
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_zip() {
158        let result1: Result<i32, &str> = Ok(5);
159        let result2: Result<String, &str> = Ok("hello".to_string());
160
161        let zipped = zip(result1, result2);
162        assert_eq!(zipped, Ok((5, "hello".to_string())));
163
164        let error_result: Result<i32, &str> = Err("error");
165        let zipped_error = zip(error_result, result2);
166        assert_eq!(zipped_error, Err("error"));
167    }
168
169    #[test]
170    fn test_zip_with() {
171        let result1: Result<i32, &str> = Ok(5);
172        let result2: Result<i32, &str> = Ok(3);
173
174        let zipped = zip_with(result1, result2, |a, b| a + b);
175        assert_eq!(zipped, Ok(8));
176    }
177
178    #[test]
179    fn test_zip3() {
180        let result1: Result<i32, &str> = Ok(1);
181        let result2: Result<i32, &str> = Ok(2);
182        let result3: Result<i32, &str> = Ok(3);
183
184        let zipped = zip3(result1, result2, result3);
185        assert_eq!(zipped, Ok((1, 2, 3)));
186    }
187
188    #[test]
189    fn test_zip3_with() {
190        let result1: Result<i32, &str> = Ok(1);
191        let result2: Result<i32, &str> = Ok(2);
192        let result3: Result<i32, &str> = Ok(3);
193
194        let zipped = zip3_with(result1, result2, result3, |a, b, c| a + b + c);
195        assert_eq!(zipped, Ok(6));
196    }
197
198    #[test]
199    fn test_sequence() {
200        let results = vec![Ok(1), Ok(2), Ok(3)];
201        let sequenced = sequence(results);
202        assert_eq!(sequenced, Ok(vec![1, 2, 3]));
203
204        let error_results = vec![Ok(1), Err("error"), Ok(3)];
205        let sequenced_error = sequence(error_results);
206        assert_eq!(sequenced_error, Err("error"));
207    }
208
209    #[test]
210    fn test_flat_map() {
211        let result: Result<i32, &str> = Ok(5);
212        let mapped = flat_map(result, |x| Ok(x * 2));
213        assert_eq!(mapped, Ok(10));
214
215        let error_result: Result<i32, &str> = Err("error");
216        let mapped_error = flat_map(error_result, |x| Ok(x * 2));
217        assert_eq!(mapped_error, Err("error"));
218    }
219
220    #[test]
221    fn test_map() {
222        let result: Result<i32, &str> = Ok(5);
223        let mapped = map(result, |x| x * 2);
224        assert_eq!(mapped, Ok(10));
225    }
226
227    #[test]
228    fn test_map_error() {
229        let result: Result<i32, &str> = Err("error");
230        let mapped = map_error(result, |e| format!("Error: {}", e));
231        assert_eq!(mapped, Err("Error: error".to_string()));
232    }
233
234    #[test]
235    fn test_get_or_else() {
236        let result: Result<i32, &str> = Ok(5);
237        let value = get_or_else(result, || 0);
238        assert_eq!(value, 5);
239
240        let error_result: Result<i32, &str> = Err("error");
241        let value_error = get_or_else(error_result, || 0);
242        assert_eq!(value_error, 0);
243    }
244
245    #[test]
246    fn test_from_option() {
247        let some_option = Some(5);
248        let result = from_option(some_option, "error");
249        assert_eq!(result, Ok(5));
250
251        let none_option: Option<i32> = None;
252        let result_none = from_option(none_option, "error");
253        assert_eq!(result_none, Err("error"));
254    }
255
256    #[test]
257    fn test_to_option() {
258        let result: Result<i32, &str> = Ok(5);
259        let option = to_option(result);
260        assert_eq!(option, Some(5));
261
262        let error_result: Result<i32, &str> = Err("error");
263        let option_error = to_option(error_result);
264        assert_eq!(option_error, None);
265    }
266
267    #[test]
268    fn test_or() {
269        let result1: Result<i32, &str> = Ok(5);
270        let result2: Result<i32, &str> = Ok(10);
271        let combined = or(result1, result2);
272        assert_eq!(combined, Ok(5));
273
274        let error_result1: Result<i32, &str> = Err("error1");
275        let result2: Result<i32, &str> = Ok(10);
276        let combined_error = or(error_result1, result2);
277        assert_eq!(combined_error, Ok(10));
278    }
279
280    #[test]
281    fn test_or_else() {
282        let result: Result<i32, &str> = Ok(5);
283        let fallback = or_else(result, || Ok(10));
284        assert_eq!(fallback, Ok(5));
285
286        let error_result: Result<i32, &str> = Err("error");
287        let fallback_error = or_else(error_result, || Ok(10));
288        assert_eq!(fallback_error, Ok(10));
289    }
290}