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    getter: impl Fn(&Root) -> Value + Clone + 'static,
49) -> impl Fn(&Root, &Root) -> bool
50where
51    Value: Ord + Clone,
52    Root: Clone,
53{
54    their(getter, |a: Value, b: Value| a < b)
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[derive(Debug, Clone, PartialEq)]
62    struct User {
63        name: String,
64        age: u32,
65    }
66
67    #[test]
68    fn test_combining() {
69        let user = User {
70            name: "Alice".into(),
71            age: 30,
72        };
73        let f = combining(|u: &User| u.age, |a, b| a + b);
74        assert_eq!(f(10, &user), 40); // 10 + user.age
75    }
76
77    #[test]
78    fn test_combining_mut() {
79        let user = User {
80            name: "Bob".into(),
81            age: 5,
82        };
83        let mut value = 10;
84        let f = combining_mut(|u: &User| u.age, |a: &mut u32, b: u32| *a += b);
85        f(&mut value, &user);
86        assert_eq!(value, 15);
87    }
88
89    #[test]
90    fn test_their() {
91        let alice = User {
92            name: "Alice".into(),
93            age: 20,
94        };
95        let bob = User {
96            name: "Bob".into(),
97            age: 25,
98        };
99
100        let cmp = their(|u: &User| u.age, |a, b| a.max(b));
101        assert_eq!(cmp(&alice, &bob), 25);
102    }
103
104    #[test]
105    fn test_their_cmp() {
106        let alice = User {
107            name: "Alice".into(),
108            age: 20,
109        };
110        let bob = User {
111            name: "Bob".into(),
112            age: 25,
113        };
114
115        let cmp = their_cmp(|u: &User| u.age);
116        assert_eq!(cmp(&alice, &bob), true); // 20 < 25
117        assert_eq!(cmp(&bob, &alice), false); // 25 < 20
118    }
119}