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}