overture_core/
with.rs

1// Left-to-right function application utilities
2// Equivalent to Swift's with function
3
4/// Left-to-right function application.
5/// Equivalent to Swift's with<A, B>(_ a: A, _ f: (A) throws -> B) rethrows -> B
6///
7/// # Examples
8/// ```
9/// use overture_core::with::with;
10/// 
11/// let result = with(5, |x| x * 2);
12/// assert_eq!(result, 10);
13/// ```
14pub fn with<A, B>(a: A, f: impl FnOnce(A) -> B) -> B {
15    f(a)
16}
17
18/// Left-to-right function application with error handling.
19/// Equivalent to Swift's with<A, B>(_ a: A, _ f: (A) throws -> B) rethrows -> B
20///
21/// # Examples
22/// ```
23/// use overture_core::with::with_throwing;
24/// 
25/// let result = with_throwing(5, |x| {
26///     if x > 0 { Ok(x * 2) } else { Err("Negative number") }
27/// });
28/// assert_eq!(result, Ok(10));
29/// ```
30pub fn with_throwing<A, B, E>(a: A, f: impl FnOnce(A) -> Result<B, E>) -> Result<B, E> {
31    f(a)
32}
33
34/// Left-to-right function application with mutable reference.
35/// Applies a function that takes a mutable reference and returns the modified value.
36///
37/// # Examples
38/// ```
39/// use overture_core::with::with_mut;
40/// 
41/// let result = with_mut(vec![1, 2, 3], |v| v.push(4));
42/// assert_eq!(result, vec![1, 2, 3, 4]);
43/// ```
44pub fn with_mut<A>(mut a: A, f: impl FnOnce(&mut A)) -> A {
45    f(&mut a);
46    a
47}
48
49/// Left-to-right function application with mutable reference and error handling.
50pub fn with_mut_throwing<A, E>(mut a: A, f: impl FnOnce(&mut A) -> Result<(), E>) -> Result<A, E> {
51    f(&mut a)?;
52    Ok(a)
53}
54
55/// Left-to-right function application with reference.
56/// Applies a function that takes a reference and returns the original value.
57///
58/// # Examples
59/// ```
60/// use overture_core::with::with_ref;
61/// 
62/// let result = with_ref(vec![1, 2, 3], |v| println!("Length: {}", v.len()));
63/// assert_eq!(result, vec![1, 2, 3]);
64/// ```
65pub fn with_ref<A>(a: A, f: impl FnOnce(&A)) -> A {
66    f(&a);
67    a
68}
69
70/// Left-to-right function application with reference and error handling.
71pub fn with_ref_throwing<A, E>(a: A, f: impl FnOnce(&A) -> Result<(), E>) -> Result<A, E> {
72    f(&a)?;
73    Ok(a)
74}
75
76/// Chain multiple with operations for fluent API.
77/// Applies multiple functions in sequence, passing the result of each to the next.
78///
79/// # Examples
80/// ```
81/// use overture_core::with::with_chain;
82/// 
83/// let result = with_chain(5, [
84///     |x| x * 2,
85///     |x| x + 1,
86///     |x| x * 3,
87/// ]);
88/// assert_eq!(result, 33); // ((5 * 2) + 1) * 3 = 33
89/// ```
90pub fn with_chain<A, F>(a: A, functions: impl IntoIterator<Item = F>) -> A
91where
92    F: FnOnce(A) -> A,
93{
94    functions.into_iter().fold(a, |acc, f| f(acc))
95}
96
97/// Chain multiple with operations with error handling.
98pub fn with_chain_throwing<A, E, F>(a: A, functions: impl IntoIterator<Item = F>) -> Result<A, E>
99where
100    F: FnOnce(A) -> Result<A, E>,
101{
102    functions.into_iter().try_fold(a, |acc, f| f(acc))
103}
104
105/// Apply a function and return both the result and the original value.
106///
107/// # Examples
108/// ```
109/// use overture_core::with::with_tap;
110/// 
111/// let (result, original) = with_tap(5, |x| x * 2);
112/// assert_eq!(result, 10);
113/// assert_eq!(original, 5);
114/// ```
115pub fn with_tap<A, B>(a: A, f: impl FnOnce(&A) -> B) -> (B, A) {
116    let result = f(&a);
117    (result, a)
118}
119
120/// Apply a function for side effects and return the original value.
121/// Useful for debugging or logging without changing the value.
122///
123/// # Examples
124/// ```
125/// use overture_core::with::with_side_effect;
126/// 
127/// let result = with_side_effect(5, |x| println!("Value: {}", x));
128/// assert_eq!(result, 5);
129/// ```
130pub fn with_side_effect<A>(a: A, f: impl FnOnce(&A)) -> A {
131    f(&a);
132    a
133}
134
135/// Apply a function for side effects with error handling.
136pub fn with_side_effect_throwing<A, E>(a: A, f: impl FnOnce(&A) -> Result<(), E>) -> Result<A, E> {
137    f(&a)?;
138    Ok(a)
139}
140
141/// Conditional application of a function.
142/// Applies the function only if the condition is true.
143///
144/// # Examples
145/// ```
146/// use overture_core::with::with_if;
147/// 
148/// let result = with_if(5, true, |x| x * 2);
149/// assert_eq!(result, 10);
150/// 
151/// let result = with_if(5, false, |x| x * 2);
152/// assert_eq!(result, 5);
153/// ```
154pub fn with_if<A, F>(a: A, condition: bool, f: F) -> A
155where
156    F: FnOnce(A) -> A,
157{
158    if condition {
159        f(a)
160    } else {
161        a
162    }
163}
164
165/// Conditional application with error handling.
166pub fn with_if_throwing<A, E, F>(a: A, condition: bool, f: F) -> Result<A, E>
167where
168    F: FnOnce(A) -> Result<A, E>,
169{
170    if condition {
171        f(a)
172    } else {
173        Ok(a)
174    }
175}
176
177/// Apply a function with a default value if it fails.
178///
179/// # Examples
180/// ```
181/// use overture_core::with::with_default;
182/// 
183/// let result = with_default("hello", |s| s.parse::<i32>(), 0);
184/// assert_eq!(result, 0);
185/// 
186/// let result = with_default("42", |s| s.parse::<i32>(), 0);
187/// assert_eq!(result, 42);
188/// ```
189pub fn with_default<A, B, E, F>(a: A, f: F, default: B) -> B
190where
191    F: FnOnce(A) -> Result<B, E>,
192{
193    f(a).unwrap_or(default)
194}
195
196#[macro_export]
197macro_rules! with_macro {
198    ($val:expr, $f:expr) => {
199        $crate::with::with($val, $f)
200    };
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    #[test]
208    fn test_with() {
209        let result = with(5, |x| x * 2);
210        assert_eq!(result, 10);
211    }
212
213    #[test]
214    fn test_with_throwing() {
215        let result = with_throwing(5, |x| {
216            if x > 0 { Ok(x * 2) } else { Err("Negative number") }
217        });
218        assert_eq!(result, Ok(10));
219
220        let error_result = with_throwing(-1, |x| {
221            if x > 0 { Ok(x * 2) } else { Err("Negative number") }
222        });
223        assert_eq!(error_result, Err("Negative number"));
224    }
225
226    #[test]
227    fn test_with_mut() {
228        let result = with_mut(vec![1, 2, 3], |v| v.push(4));
229        assert_eq!(result, vec![1, 2, 3, 4]);
230    }
231
232    #[test]
233    fn test_with_mut_throwing() {
234        let result = with_mut_throwing(vec![1, 2, 3], |v| {
235            v.push(4);
236            Ok(())
237        });
238        assert_eq!(result, Ok(vec![1, 2, 3, 4]));
239    }
240
241    #[test]
242    fn test_with_ref() {
243        let result = with_ref(vec![1, 2, 3], |v| {
244            assert_eq!(v.len(), 3);
245        });
246        assert_eq!(result, vec![1, 2, 3]);
247    }
248
249    #[test]
250    fn test_with_chain() {
251        let result = with_chain(5, [
252            |x| x * 2,
253            |x| x + 1,
254            |x| x * 3,
255        ]);
256        assert_eq!(result, 33); // ((5 * 2) + 1) * 3 = 33
257    }
258
259    #[test]
260    fn test_with_chain_throwing() {
261        let result = with_chain_throwing(5, [
262            |x| Ok(x * 2),
263            |x| Ok(x + 1),
264            |x| Ok(x * 3),
265        ]);
266        assert_eq!(result, Ok(33));
267    }
268
269    #[test]
270    fn test_with_tap() {
271        let (result, original) = with_tap(5, |x| x * 2);
272        assert_eq!(result, 10);
273        assert_eq!(original, 5);
274    }
275
276    #[test]
277    fn test_with_side_effect() {
278        let result = with_side_effect(5, |x| {
279            assert_eq!(*x, 5);
280        });
281        assert_eq!(result, 5);
282    }
283
284    #[test]
285    fn test_with_if() {
286        let result = with_if(5, true, |x| x * 2);
287        assert_eq!(result, 10);
288
289        let result = with_if(5, false, |x| x * 2);
290        assert_eq!(result, 5);
291    }
292
293    #[test]
294    fn test_with_default() {
295        let result = with_default("hello", |s| s.parse::<i32>(), 0);
296        assert_eq!(result, 0);
297
298        let result = with_default("42", |s| s.parse::<i32>(), 0);
299        assert_eq!(result, 42);
300    }
301}