rust_overture/
lense.rs

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