1pub 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 pub fn get_fn(&self) -> impl Fn(&Root) -> &Value {
14 let g = self.get;
15 move |root| g(root)
16 }
17
18 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 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 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 let mut owned = val_ref.clone();
55 update(&mut owned);
56 (self.set)(root, owned);
57 }
58 }
59}
60
61#[cfg(test)]
87mod tests {
88 use super::*;
89 #[derive(Debug, Clone, PartialEq)]
91 struct User {
92 name: String,
93 age: u32,
94 }
95
96 fn age_lens() -> Lens<User, u32> {
98 Lens::new(|u: &User| &u.age, |u: &mut User, v: u32| u.age = v)
99 }
100
101 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}