overture_core/combinig.rs
1// Combining utilities for functional programming
2// Equivalent to Swift's combining functions
3
4/// Combines a getter function with a binary operation.
5/// Equivalent to Swift's combining<Root, Value, NewValue>(_ getter: @escaping (Root) -> Value, _ combine: @escaping (Value, Value) -> NewValue) -> (Value, Root) -> NewValue
6///
7/// # Examples
8/// ```
9/// use overture_core::combinig::combining;
10///
11/// struct User { age: u32 }
12/// let user = User { age: 30 };
13/// let f = combining(|u: &User| u.age, |a, b| a + b);
14/// assert_eq!(f(10, &user), 40); // 10 + user.age
15/// ```
16pub fn combining<Root, Value, NewValue>(
17 getter: impl Fn(&Root) -> Value + Clone + 'static,
18 combine: impl Fn(Value, Value) -> NewValue + Clone + 'static,
19) -> impl Fn(Value, &Root) -> NewValue
20where
21 Value: Clone,
22{
23 move |value: Value, root: &Root| {
24 let rhs = getter(root);
25 combine(value, rhs)
26 }
27}
28
29/// Combines a getter function with a mutable binary operation.
30/// Equivalent to Swift's combining<Root, Value>(_ getter: @escaping (Root) -> Value, _ combine: @escaping (inout Value, Value) -> Void) -> (inout Value, Root) -> Void
31///
32/// # Examples
33/// ```
34/// use overture_core::combinig::combining_mut;
35///
36/// struct User { age: u32 }
37/// let user = User { age: 5 };
38/// let mut value = 10;
39/// let f = combining_mut(|u: &User| u.age, |a: &mut u32, b: u32| *a += b);
40/// f(&mut value, &user);
41/// assert_eq!(value, 15);
42/// ```
43pub fn combining_mut<Root, Value>(
44 getter: impl Fn(&Root) -> Value + Clone + 'static,
45 combine: impl Fn(&mut Value, Value) + Clone + 'static,
46) -> impl Fn(&mut Value, &Root)
47where
48 Value: Clone,
49{
50 move |value: &mut Value, root: &Root| {
51 let rhs = getter(root);
52 combine(value, rhs);
53 }
54}
55
56/// Applies a combining function to values extracted from two roots.
57/// Equivalent to Swift's their<Root, Value, NewValue>(_ getter: @escaping (Root) -> Value, _ combining: @escaping (Value, Value) -> NewValue) -> (Root, Root) -> NewValue
58///
59/// # Examples
60/// ```
61/// use overture_core::combinig::their;
62///
63/// struct User { age: u32 }
64/// let alice = User { age: 20 };
65/// let bob = User { age: 25 };
66/// let cmp = their(|u: &User| u.age, |a, b| a.max(b));
67/// assert_eq!(cmp(&alice, &bob), 25);
68/// ```
69pub fn their<Root, Value, NewValue>(
70 getter: impl Fn(&Root) -> Value + Clone + 'static,
71 combining: impl Fn(Value, Value) -> NewValue + Clone + 'static,
72) -> impl Fn(&Root, &Root) -> NewValue
73where
74 Value: Clone,
75{
76 move |a: &Root, b: &Root| {
77 let va = getter(a);
78 let vb = getter(b);
79 combining(va, vb)
80 }
81}
82
83/// Applies a comparison function to values extracted from two roots.
84/// Equivalent to Swift's their<Root, Value: Comparable>(_ getter: @escaping (Root) -> Value) -> (Root, Root) -> Bool
85///
86/// # Examples
87/// ```
88/// use overture_core::combinig::their_cmp;
89///
90/// struct User { age: u32 }
91/// let alice = User { age: 20 };
92/// let bob = User { age: 25 };
93/// let cmp = their_cmp(|u: &User| u.age);
94/// assert_eq!(cmp(&alice, &bob), true); // 20 < 25
95/// ```
96pub fn their_cmp<Root, Value>(
97 getter: impl Fn(&Root) -> Value + Clone + 'static,
98) -> impl Fn(&Root, &Root) -> bool
99where
100 Value: Ord + Clone,
101{
102 their(getter, |a: Value, b: Value| a < b)
103}
104
105/// Applies an equality comparison to values extracted from two roots.
106/// Equivalent to Swift's their<Root, Value: Equatable>(_ getter: @escaping (Root) -> Value) -> (Root, Root) -> Bool
107///
108/// # Examples
109/// ```
110/// use overture_core::combinig::their_eq;
111///
112/// struct User { name: String }
113/// let alice = User { name: "Alice".to_string() };
114/// let bob = User { name: "Bob".to_string() };
115/// let eq = their_eq(|u: &User| &u.name);
116/// assert_eq!(eq(&alice, &bob), false);
117/// ```
118pub fn their_eq<Root, Value>(
119 getter: impl Fn(&Root) -> Value + Clone + 'static,
120) -> impl Fn(&Root, &Root) -> bool
121where
122 Value: PartialEq + Clone,
123{
124 their(getter, |a: Value, b: Value| a == b)
125}
126
127/// Applies a greater than comparison to values extracted from two roots.
128///
129/// # Examples
130/// ```
131/// use overture_core::combinig::their_gt;
132///
133/// struct User { age: u32 }
134/// let alice = User { age: 20 };
135/// let bob = User { age: 25 };
136/// let gt = their_gt(|u: &User| u.age);
137/// assert_eq!(gt(&alice, &bob), false); // 20 > 25
138/// ```
139pub fn their_gt<Root, Value>(
140 getter: impl Fn(&Root) -> Value + Clone + 'static,
141) -> impl Fn(&Root, &Root) -> bool
142where
143 Value: Ord + Clone,
144{
145 their(getter, |a: Value, b: Value| a > b)
146}
147
148/// Applies a less than or equal comparison to values extracted from two roots.
149///
150/// # Examples
151/// ```
152/// use overture_core::combinig::their_le;
153///
154/// struct User { age: u32 }
155/// let alice = User { age: 20 };
156/// let bob = User { age: 25 };
157/// let le = their_le(|u: &User| u.age);
158/// assert_eq!(le(&alice, &bob), true); // 20 <= 25
159/// ```
160pub fn their_le<Root, Value>(
161 getter: impl Fn(&Root) -> Value + Clone + 'static,
162) -> impl Fn(&Root, &Root) -> bool
163where
164 Value: Ord + Clone,
165{
166 their(getter, |a: Value, b: Value| a <= b)
167}
168
169/// Applies a greater than or equal comparison to values extracted from two roots.
170///
171/// # Examples
172/// ```
173/// use overture_core::combinig::their_ge;
174///
175/// struct User { age: u32 }
176/// let alice = User { age: 20 };
177/// let bob = User { age: 25 };
178/// let ge = their_ge(|u: &User| u.age);
179/// assert_eq!(ge(&alice, &bob), false); // 20 >= 25
180/// ```
181pub fn their_ge<Root, Value>(
182 getter: impl Fn(&Root) -> Value + Clone + 'static,
183) -> impl Fn(&Root, &Root) -> bool
184where
185 Value: Ord + Clone,
186{
187 their(getter, |a: Value, b: Value| a >= b)
188}
189
190/// Applies a maximum operation to values extracted from two roots.
191///
192/// # Examples
193/// ```
194/// use overture_core::combinig::their_max;
195///
196/// struct User { age: u32 }
197/// let alice = User { age: 20 };
198/// let bob = User { age: 25 };
199/// let max = their_max(|u: &User| u.age);
200/// assert_eq!(max(&alice, &bob), 25);
201/// ```
202pub fn their_max<Root, Value>(
203 getter: impl Fn(&Root) -> Value + Clone + 'static,
204) -> impl Fn(&Root, &Root) -> Value
205where
206 Value: Ord + Clone,
207{
208 their(getter, |a: Value, b: Value| a.max(b))
209}
210
211/// Applies a minimum operation to values extracted from two roots.
212///
213/// # Examples
214/// ```
215/// use overture_core::combinig::their_min;
216///
217/// struct User { age: u32 }
218/// let alice = User { age: 20 };
219/// let bob = User { age: 25 };
220/// let min = their_min(|u: &User| u.age);
221/// assert_eq!(min(&alice, &bob), 20);
222/// ```
223pub fn their_min<Root, Value>(
224 getter: impl Fn(&Root) -> Value + Clone + 'static,
225) -> impl Fn(&Root, &Root) -> Value
226where
227 Value: Ord + Clone,
228{
229 their(getter, |a: Value, b: Value| a.min(b))
230}
231
232/// Applies an addition operation to values extracted from two roots.
233///
234/// # Examples
235/// ```
236/// use overture_core::combinig::their_add;
237///
238/// struct User { score: u32 }
239/// let alice = User { score: 100 };
240/// let bob = User { score: 200 };
241/// let add = their_add(|u: &User| u.score);
242/// assert_eq!(add(&alice, &bob), 300);
243/// ```
244pub fn their_add<Root, Value>(
245 getter: impl Fn(&Root) -> Value + Clone + 'static,
246) -> impl Fn(&Root, &Root) -> Value
247where
248 Value: std::ops::Add<Output = Value> + Clone,
249{
250 their(getter, |a: Value, b: Value| a + b)
251}
252
253/// Applies a subtraction operation to values extracted from two roots.
254///
255/// # Examples
256/// ```
257/// use overture_core::combinig::their_sub;
258///
259/// struct User { score: u32 }
260/// let alice = User { score: 200 };
261/// let bob = User { score: 100 };
262/// let sub = their_sub(|u: &User| u.score);
263/// assert_eq!(sub(&alice, &bob), 100);
264/// ```
265pub fn their_sub<Root, Value>(
266 getter: impl Fn(&Root) -> Value + Clone + 'static,
267) -> impl Fn(&Root, &Root) -> Value
268where
269 Value: std::ops::Sub<Output = Value> + Clone,
270{
271 their(getter, |a: Value, b: Value| a - b)
272}
273
274/// Applies a multiplication operation to values extracted from two roots.
275///
276/// # Examples
277/// ```
278/// use overture_core::combinig::their_mul;
279///
280/// struct User { multiplier: u32 }
281/// let alice = User { multiplier: 3 };
282/// let bob = User { multiplier: 4 };
283/// let mul = their_mul(|u: &User| u.multiplier);
284/// assert_eq!(mul(&alice, &bob), 12);
285/// ```
286pub fn their_mul<Root, Value>(
287 getter: impl Fn(&Root) -> Value + Clone + 'static,
288) -> impl Fn(&Root, &Root) -> Value
289where
290 Value: std::ops::Mul<Output = Value> + Clone,
291{
292 their(getter, |a: Value, b: Value| a * b)
293}
294
295/// Applies a division operation to values extracted from two roots.
296///
297/// # Examples
298/// ```
299/// use overture_core::combinig::their_div;
300///
301/// struct User { value: f64 }
302/// let alice = User { value: 12.0 };
303/// let bob = User { value: 3.0 };
304/// let div = their_div(|u: &User| u.value);
305/// assert_eq!(div(&alice, &bob), 4.0);
306/// ```
307pub fn their_div<Root, Value>(
308 getter: impl Fn(&Root) -> Value + Clone + 'static,
309) -> impl Fn(&Root, &Root) -> Value
310where
311 Value: std::ops::Div<Output = Value> + Clone,
312{
313 their(getter, |a: Value, b: Value| a / b)
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[derive(Debug, Clone, PartialEq)]
321 struct User {
322 name: String,
323 age: u32,
324 score: f64,
325 }
326
327 #[test]
328 fn test_combining() {
329 let user = User {
330 name: "Alice".into(),
331 age: 30,
332 score: 85.5,
333 };
334 let f = combining(|u: &User| u.age, |a, b| a + b);
335 assert_eq!(f(10, &user), 40); // 10 + user.age
336 }
337
338 #[test]
339 fn test_combining_mut() {
340 let user = User {
341 name: "Bob".into(),
342 age: 5,
343 score: 90.0,
344 };
345 let mut value = 10;
346 let f = combining_mut(|u: &User| u.age, |a: &mut u32, b: u32| *a += b);
347 f(&mut value, &user);
348 assert_eq!(value, 15);
349 }
350
351 #[test]
352 fn test_their() {
353 let alice = User {
354 name: "Alice".into(),
355 age: 20,
356 score: 85.0,
357 };
358 let bob = User {
359 name: "Bob".into(),
360 age: 25,
361 score: 90.0,
362 };
363
364 let cmp = their(|u: &User| u.age, |a, b| a.max(b));
365 assert_eq!(cmp(&alice, &bob), 25);
366 }
367
368 #[test]
369 fn test_their_cmp() {
370 let alice = User {
371 name: "Alice".into(),
372 age: 20,
373 score: 85.0,
374 };
375 let bob = User {
376 name: "Bob".into(),
377 age: 25,
378 score: 90.0,
379 };
380
381 let cmp = their_cmp(|u: &User| u.age);
382 assert_eq!(cmp(&alice, &bob), true); // 20 < 25
383 assert_eq!(cmp(&bob, &alice), false); // 25 < 20
384 }
385
386 #[test]
387 fn test_their_eq() {
388 let alice = User {
389 name: "Alice".into(),
390 age: 20,
391 score: 85.0,
392 };
393 let bob = User {
394 name: "Bob".into(),
395 age: 25,
396 score: 90.0,
397 };
398
399 let eq = their_eq(|u: &User| &u.name);
400 assert_eq!(eq(&alice, &bob), false);
401 assert_eq!(eq(&alice, &alice), true);
402 }
403
404 #[test]
405 fn test_their_max() {
406 let alice = User {
407 name: "Alice".into(),
408 age: 20,
409 score: 85.0,
410 };
411 let bob = User {
412 name: "Bob".into(),
413 age: 25,
414 score: 90.0,
415 };
416
417 let max = their_max(|u: &User| u.age);
418 assert_eq!(max(&alice, &bob), 25);
419 }
420
421 #[test]
422 fn test_their_min() {
423 let alice = User {
424 name: "Alice".into(),
425 age: 20,
426 score: 85.0,
427 };
428 let bob = User {
429 name: "Bob".into(),
430 age: 25,
431 score: 90.0,
432 };
433
434 let min = their_min(|u: &User| u.age);
435 assert_eq!(min(&alice, &bob), 20);
436 }
437
438 #[test]
439 fn test_their_add() {
440 let alice = User {
441 name: "Alice".into(),
442 age: 20,
443 score: 85.0,
444 };
445 let bob = User {
446 name: "Bob".into(),
447 age: 25,
448 score: 90.0,
449 };
450
451 let add = their_add(|u: &User| u.age);
452 assert_eq!(add(&alice, &bob), 45);
453 }
454
455 #[test]
456 fn test_their_mul() {
457 let alice = User {
458 name: "Alice".into(),
459 age: 3,
460 score: 85.0,
461 };
462 let bob = User {
463 name: "Bob".into(),
464 age: 4,
465 score: 90.0,
466 };
467
468 let mul = their_mul(|u: &User| u.age);
469 assert_eq!(mul(&alice, &bob), 12);
470 }
471}