rust_overture/
combinig.rs

1/// Equivalent of Swift `combining(getter, combine)`
2/// Takes a getter and a binary function, returns a new function `(Value, Root) -> NewValue`
3pub fn combining<Root, Value, NewValue>(
4    getter: impl Fn(&Root) -> Value + Clone + 'static,
5    combine: impl Fn(Value, Value) -> NewValue + Clone + 'static,
6) -> impl Fn(Value, &Root) -> NewValue
7where
8    Value: Clone,
9{
10    move |value: Value, root: &Root| {
11        let rhs = getter(root);
12        combine(value, rhs)
13    }
14}
15
16/// Variant: in-place combine (like Swift `inout Value`)
17pub fn combining_mut<Root, Value>(
18    getter: impl Fn(&Root) -> Value + Clone + 'static,
19    combine: impl Fn(&mut Value, Value) + Clone + 'static,
20) -> impl Fn(&mut Value, &Root)
21where
22    Value: Clone,
23{
24    move |value: &mut Value, root: &Root| {
25        let rhs = getter(root);
26        combine(value, rhs);
27    }
28}
29
30/// Equivalent of Swift `their(getter, combine)`
31/// Takes a getter and binary op, produces `(Root, Root) -> NewValue`
32pub fn their<Root, Value, NewValue>(
33    getter: impl Fn(&Root) -> Value + Clone + 'static,
34    combine: impl Fn(Value, Value) -> NewValue + Clone + 'static,
35) -> impl Fn(&Root, &Root) -> NewValue
36where
37    Value: Clone,
38{
39    move |a: &Root, b: &Root| {
40        let va = getter(a);
41        let vb = getter(b);
42        combine(va, vb)
43    }
44}
45
46/// Specialization: `their(getter)` for Comparable → `(Root, Root) -> bool`
47pub fn their_cmp<Root, Value>
48(
49    getter: impl Fn(&Root) -> Value + Clone + 'static,
50) -> impl Fn(&Root, &Root) -> bool
51where
52    Value: Ord + Clone,
53    Root: Clone
54
55{
56    their(getter, |a: Value, b: Value| a < b)
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[derive(Debug, Clone, PartialEq)]
64    struct User {
65        name: String,
66        age: u32,
67    }
68
69    #[test]
70    fn test_combining() {
71        let user = User { name: "Alice".into(), age: 30 };
72        let f = combining(|u: &User| u.age, |a, b| a + b);
73        assert_eq!(f(10, &user), 40); // 10 + user.age
74    }
75
76    #[test]
77    fn test_combining_mut() {
78        let user = User { name: "Bob".into(), age: 5 };
79        let mut value = 10;
80        let f = combining_mut(|u: &User| u.age, |a: &mut u32, b: u32| *a += b);
81        f(&mut value, &user);
82        assert_eq!(value, 15);
83    }
84
85    #[test]
86    fn test_their() {
87        let alice = User { name: "Alice".into(), age: 20 };
88        let bob = User { name: "Bob".into(), age: 25 };
89
90        let cmp = their(|u: &User| u.age, |a, b| a.max(b));
91        assert_eq!(cmp(&alice, &bob), 25);
92    }
93
94    #[test]
95    fn test_their_cmp() {
96        let alice = User { name: "Alice".into(), age: 20 };
97        let bob = User { name: "Bob".into(), age: 25 };
98
99        let cmp = their_cmp(|u: &User| u.age);
100        assert_eq!(cmp(&alice, &bob), true);  // 20 < 25
101        assert_eq!(cmp(&bob, &alice), false); // 25 < 20
102    }
103}