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