1
2pub 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 pub fn get_fn(&self) -> impl Fn(&Root) -> &Value {
16 let g = self.get;
17 move |root| g(root)
18 }
19
20 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 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 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 let mut owned = val_ref.clone();
57 update(&mut owned);
58 (self.set)(root, owned);
59 }
60 }
61}
62
63
64#[cfg(test)]
92mod tests {
93 use super::*;
94#[derive(Debug, Clone, PartialEq)]
96struct User {
97 name: String,
98 age: u32,
99}
100
101fn age_lens() -> Lens<User, u32> {
103 Lens::new(|u: &User| &u.age, |u: &mut User, v: u32| u.age = v)
104}
105
106fn 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}