rust_overture/
keypath.rs

1
2/// A Lens represents a getter + setter for a field in `Root`.
3pub struct Lens<Root, Value> {
4    pub get: fn(&Root) -> &Value,
5    pub set: fn(&mut Root, Value),
6}
7
8impl<Root, Value> Lens<Root, Value> 
9{
10    pub fn new(get: fn(&Root) -> &Value, set: fn(&mut Root, Value)) -> Self {
11        Self { get, set }
12    }
13
14    /// Getter: like Swift `get(\.field)`
15    pub fn get_fn(&self) -> impl Fn(&Root) -> &Value {
16        let g = self.get;
17        move |root| g(root)
18    }
19
20    /// Immutable setter: like Swift `prop(\.field)`
21    pub fn over(&self, update: impl Fn(Value) -> Value + 'static + Clone) -> impl Fn(Root) -> Root
22    where
23        Root: Clone,
24        Value: Clone,
25    {
26        let get = self.get;
27        let set = self.set;
28        move |mut root: Root| {
29            let old_value = get(&root).clone();
30            let new_value = update(old_value);
31            set(&mut root, new_value);
32            root
33        }
34    }
35
36    /// Set a constant value: like Swift `set(\.field, value)`
37    pub fn set_value(&self, value: Value) -> impl Fn(Root) -> Root
38    where
39        Root: Clone,
40        Value: Clone + 'static,
41    {
42        self.over(move |_| value.clone())
43    }
44
45    /// Mutable in-place setter: like Swift `mprop`
46    pub fn mver(&self, update: impl Fn(&mut Value) + 'static + Clone) -> impl Fn(&mut Root) 
47    where 
48        Root: Clone,
49        Value: Clone
50{
51        let get = self.get;
52        move |root: &mut Root| {
53            let val_ref: &Value = get(root);
54            // SAFETY: we need a mutable access, so assume `set` works with ownership
55            // Simplify: clone, mutate, reassign
56            let mut owned = val_ref.clone();
57            update(&mut owned);
58            (self.set)(root, owned);
59        }
60    }
61}
62
63
64// fn main() {
65//     let user = User {
66//         name: "Alice".into(),
67//         age: 30,
68//     };
69
70//     let lens = age_lens();
71
72//     // getter
73//     println!("Age = {}", (lens.get_fn())(&user));
74
75//     // immutable update
76//     let older = (lens.over(|age| age + 1))(user.clone());
77//     println!("Older = {:?}", older);
78
79//     // set constant
80//     let teenager = (lens.set_value(13))(user.clone());
81//     println!("Teenager = {:?}", teenager);
82
83//     // mutable update
84//     let mut mutable_user = user.clone();
85//     (lens.mver(|age| *age += 5))(&mut mutable_user);
86//     println!("Mutated = {:?}", mutable_user);
87// }
88
89
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94/// Example struct
95#[derive(Debug, Clone, PartialEq)]
96struct User {
97    name: String,
98    age: u32,
99}
100
101// Example: lens for User::age
102fn age_lens() -> Lens<User, u32> {
103    Lens::new(|u: &User| &u.age, |u: &mut User, v: u32| u.age = v)
104}
105
106/// Lens for `User::name`
107fn name_lens() -> Lens<User, String> {
108    Lens::new(|u: &User| &u.name, |u: &mut User, v: String| u.name = v)
109}
110
111
112    #[test]
113    fn test_getter() {
114        let user = User { name: "Alice".into(), age: 30 };
115        let lens = age_lens();
116        assert_eq!((lens.get_fn())(&user), &30);
117    }
118
119    #[test]
120    fn test_immutable_update() {
121        let user = User { name: "Alice".into(), age: 30 };
122        let lens = age_lens();
123        let updated = (lens.over(|age| age + 1))(user.clone());
124        assert_eq!(updated.age, 31);
125        assert_eq!(user.age, 30, "original must remain unchanged");
126    }
127
128    #[test]
129    fn test_set_constant() {
130        let user = User { name: "Bob".into(), age: 40 };
131        let lens = age_lens();
132        let teenager = (lens.set_value(13))(user.clone());
133        assert_eq!(teenager.age, 13);
134    }
135
136    #[test]
137    fn test_mutable_update_inplace() {
138        let mut user = User { name: "Charlie".into(), age: 20 };
139        let lens = age_lens();
140        (lens.mver(|age| *age += 5))(&mut user);
141        assert_eq!(user.age, 25);
142    }
143
144    #[test]
145    fn test_edgecase_noop_update() {
146        let user = User { name: "Dave".into(), age: 99 };
147        let lens = age_lens();
148        let same = (lens.over(|age| age))(user.clone());
149        assert_eq!(same, user, "no-op update should not change user");
150    }
151
152    #[test]
153    fn test_edgecase_empty_string() {
154        let user = User { name: "".into(), age: 10 };
155        let lens = name_lens();
156        let filled = (lens.set_value("Zed".into()))(user.clone());
157        assert_eq!(filled.name, "Zed");
158    }
159
160    #[test]
161    fn test_edgecase_large_age() {
162        let user = User { name: "Max".into(), age: u32::MAX };
163        let lens = age_lens();
164        let wrapped = (lens.over(|age| age.saturating_add(1)))(user.clone());
165        assert_eq!(wrapped.age, u32::MAX, "should saturate at max value");
166    }
167}