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}