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 { f(a) } else { a }
159}
160
161/// Conditional application with error handling.
162pub fn with_if_throwing<A, E, F>(a: A, condition: bool, f: F) -> Result<A, E>
163where
164    F: FnOnce(A) -> Result<A, E>,
165{
166    if condition { f(a) } else { Ok(a) }
167}
168
169/// Apply a function with a default value if it fails.
170///
171/// # Examples
172/// ```
173/// use overture_core::with::with_default;
174///
175/// let result = with_default("hello", |s| s.parse::<i32>(), 0);
176/// assert_eq!(result, 0);
177///
178/// let result = with_default("42", |s| s.parse::<i32>(), 0);
179/// assert_eq!(result, 42);
180/// ```
181pub fn with_default<A, B, E, F>(a: A, f: F, default: B) -> B
182where
183    F: FnOnce(A) -> Result<B, E>,
184{
185    f(a).unwrap_or(default)
186}
187
188#[macro_export]
189macro_rules! with_macro {
190    ($val:expr, $f:expr) => {
191        $crate::with::with($val, $f)
192    };
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_with() {
201        let result = with(5, |x| x * 2);
202        assert_eq!(result, 10);
203    }
204
205    #[test]
206    fn test_with_throwing() {
207        let result = with_throwing(5, |x| {
208            if x > 0 {
209                Ok(x * 2)
210            } else {
211                Err("Negative number")
212            }
213        });
214        assert_eq!(result, Ok(10));
215
216        let error_result = with_throwing(-1, |x| {
217            if x > 0 {
218                Ok(x * 2)
219            } else {
220                Err("Negative number")
221            }
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, [|x| x * 2, |x| x + 1, |x| x * 3]);
252        assert_eq!(result, 33); // ((5 * 2) + 1) * 3 = 33
253    }
254
255    #[test]
256    fn test_with_chain_throwing() {
257        let result = with_chain_throwing(5, [|x| Ok(x * 2), |x| Ok(x + 1), |x| Ok(x * 3)]);
258        assert_eq!(result, Ok(33));
259    }
260
261    #[test]
262    fn test_with_tap() {
263        let (result, original) = with_tap(5, |x| x * 2);
264        assert_eq!(result, 10);
265        assert_eq!(original, 5);
266    }
267
268    #[test]
269    fn test_with_side_effect() {
270        let result = with_side_effect(5, |x| {
271            assert_eq!(*x, 5);
272        });
273        assert_eq!(result, 5);
274    }
275
276    #[test]
277    fn test_with_if() {
278        let result = with_if(5, true, |x| x * 2);
279        assert_eq!(result, 10);
280
281        let result = with_if(5, false, |x| x * 2);
282        assert_eq!(result, 5);
283    }
284
285    #[test]
286    fn test_with_default() {
287        let result = with_default("hello", |s| s.parse::<i32>(), 0);
288        assert_eq!(result, 0);
289
290        let result = with_default("42", |s| s.parse::<i32>(), 0);
291        assert_eq!(result, 42);
292    }
293}