1use std::marker::PhantomData;
11
12pub use crate::path::LensPath;
13
14pub trait Lens {
17 type Source;
19
20 type Target;
22
23 fn path(&self) -> LensPath;
25
26 #[doc(hidden)]
29 fn mutate(&self, source: &mut Self::Source, target: Self::Target);
30
31 fn set(&self, source: Self::Source, target: Self::Target) -> Self::Source {
33 let mut mutable_source = source;
34 self.mutate(&mut mutable_source, target);
35 mutable_source
36 }
37}
38
39pub trait RefLens: Lens {
41 fn get_ref<'a>(&self, source: &'a Self::Source) -> &'a Self::Target;
43
44 #[doc(hidden)]
47 fn get_mut_ref<'a>(&self, source: &'a mut Self::Source) -> &'a mut Self::Target;
48
49 fn mutate_with_fn(&self, source: &mut Self::Source, f: &dyn Fn(&Self::Target) -> Self::Target) {
51 let target = f(self.get_ref(source));
52 self.mutate(source, target);
53 }
54
55 fn modify(
57 &self,
58 source: Self::Source,
59 f: &dyn Fn(&Self::Target) -> Self::Target,
60 ) -> Self::Source {
61 let mut mutable_source = source;
62 self.mutate_with_fn(&mut mutable_source, f);
63 mutable_source
64 }
65}
66
67pub trait ValueLens: Lens {
69 fn get(&self, source: &Self::Source) -> Self::Target;
71}
72
73#[doc(hidden)]
77pub fn mutate_with_fn<L: RefLens, F>(lens: &L, source: &mut L::Source, f: F)
78where
79 F: Fn(&L::Target) -> L::Target,
80{
81 let target = f(lens.get_ref(source));
82 lens.mutate(source, target);
83}
84
85pub fn modify<L: RefLens, F>(lens: &L, source: L::Source, f: F) -> L::Source
89where
90 F: Fn(&L::Target) -> L::Target,
91{
92 let mut mutable_source = source;
93 mutate_with_fn(lens, &mut mutable_source, f);
94 mutable_source
95}
96
97impl<L: Lens + ?Sized> Lens for Box<L> {
98 type Source = L::Source;
99 type Target = L::Target;
100
101 #[inline(always)]
102 fn path(&self) -> LensPath {
103 (**self).path()
104 }
105
106 #[inline(always)]
107 fn mutate(&self, source: &mut L::Source, target: L::Target) {
108 (**self).mutate(source, target)
109 }
110}
111
112impl<L: RefLens + ?Sized> RefLens for Box<L> {
113 #[inline(always)]
114 fn get_ref<'a>(&self, source: &'a L::Source) -> &'a L::Target {
115 (**self).get_ref(source)
116 }
117
118 #[inline(always)]
119 fn get_mut_ref<'a>(&self, source: &'a mut L::Source) -> &'a mut L::Target {
120 (**self).get_mut_ref(source)
121 }
122}
123
124impl<L: ValueLens + ?Sized> ValueLens for Box<L> {
125 #[inline(always)]
126 fn get(&self, source: &L::Source) -> L::Target {
127 (**self).get(source)
128 }
129}
130
131pub const fn vec_lens<T>(index: usize) -> VecLens<T> {
133 VecLens {
134 index,
135 _marker: PhantomData,
136 }
137}
138
139#[doc(hidden)]
140pub const fn vec_lens_from_marker<T>(_: PhantomData<T>, index: usize) -> VecLens<T> {
141 vec_lens(index)
142}
143
144pub struct VecLens<T> {
146 index: usize,
147 _marker: PhantomData<T>,
148}
149
150impl<T> VecLens<T> {
151 fn missing_index_message(index: usize) -> String {
152 format!("vector lens index {index} is out of bounds")
153 }
154}
155
156impl<T> Lens for VecLens<T> {
157 type Source = Vec<T>;
158 type Target = T;
159
160 #[inline(always)]
161 fn path(&self) -> LensPath {
162 LensPath::from_index(self.index)
163 }
164
165 #[inline(always)]
166 fn mutate(&self, source: &mut Vec<T>, target: T) {
167 let slot = source
168 .get_mut(self.index)
169 .unwrap_or_else(|| panic!("{}", Self::missing_index_message(self.index)));
170 *slot = target;
171 }
172}
173
174impl<T> RefLens for VecLens<T> {
175 #[inline(always)]
176 fn get_ref<'a>(&self, source: &'a Vec<T>) -> &'a T {
177 source
178 .get(self.index)
179 .unwrap_or_else(|| panic!("{}", Self::missing_index_message(self.index)))
180 }
181
182 #[inline(always)]
183 fn get_mut_ref<'a>(&self, source: &'a mut Vec<T>) -> &'a mut T {
184 source
185 .get_mut(self.index)
186 .unwrap_or_else(|| panic!("{}", Self::missing_index_message(self.index)))
187 }
188}
189
190pub fn compose<LHS, RHS>(lhs: LHS, rhs: RHS) -> ComposedLens<LHS, RHS>
192where
193 LHS: RefLens,
194 LHS::Target: 'static,
195 RHS: Lens<Source = LHS::Target>,
196{
197 ComposedLens { lhs, rhs }
198}
199
200pub struct ComposedLens<LHS, RHS> {
207 lhs: LHS,
208 rhs: RHS,
209}
210
211impl<LHS, RHS> Lens for ComposedLens<LHS, RHS>
212where
213 LHS: RefLens,
214 LHS::Target: 'static,
215 RHS: Lens<Source = LHS::Target>,
216{
217 type Source = LHS::Source;
218 type Target = RHS::Target;
219
220 #[inline(always)]
221 fn path(&self) -> LensPath {
222 LensPath::concat(self.lhs.path(), self.rhs.path())
223 }
224
225 #[inline(always)]
226 fn mutate(&self, source: &mut LHS::Source, target: RHS::Target) {
227 let rhs_source = self.lhs.get_mut_ref(source);
228 self.rhs.mutate(rhs_source, target)
229 }
230}
231
232impl<LHS, RHS> RefLens for ComposedLens<LHS, RHS>
233where
234 LHS: RefLens,
235 LHS::Target: 'static,
236 RHS: RefLens<Source = LHS::Target>,
237{
238 #[inline(always)]
239 fn get_ref<'a>(&self, source: &'a LHS::Source) -> &'a RHS::Target {
240 self.rhs.get_ref(self.lhs.get_ref(source))
241 }
242
243 #[inline(always)]
244 fn get_mut_ref<'a>(&self, source: &'a mut LHS::Source) -> &'a mut RHS::Target {
245 self.rhs.get_mut_ref(self.lhs.get_mut_ref(source))
246 }
247}
248
249impl<LHS, RHS> ValueLens for ComposedLens<LHS, RHS>
250where
251 LHS: RefLens,
252 LHS::Target: 'static,
253 RHS: ValueLens<Source = LHS::Target>,
254{
255 #[inline(always)]
256 fn get(&self, source: &LHS::Source) -> RHS::Target {
257 self.rhs.get(self.lhs.get_ref(source))
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use crate::{Lens, LensPath, Lenses, RefLens, ValueLens, compose_lens, modify, vec_lens};
264
265 #[derive(Clone, Debug, PartialEq, Lenses)]
266 struct Struct1 {
267 int32: i32,
268 int16: i16,
269 }
270
271 #[derive(Clone, Debug, PartialEq, Lenses)]
272 struct Struct2 {
273 int32: i32,
274 string: String,
275 struct1: Struct1,
276 }
277
278 #[derive(Clone, Debug, PartialEq, Lenses)]
279 struct Struct3 {
280 int32: i32,
281 struct2: Struct2,
282 }
283
284 #[derive(Clone, Debug, PartialEq, Lenses)]
285 struct Struct4 {
286 inner_vec: Vec<Struct1>,
287 }
288
289 fn make_struct3() -> Struct3 {
290 Struct3 {
291 int32: 332,
292 struct2: Struct2 {
293 int32: 232,
294 string: "hi".to_string(),
295 struct1: Struct1 {
296 int32: 132,
297 int16: 116,
298 },
299 },
300 }
301 }
302
303 #[test]
304 fn a_basic_lens_should_work() {
305 let lens = crate::lens!(Struct3.struct2.struct1.int32);
306
307 let s3_0 = make_struct3();
308 assert_eq!(*lens.get_ref(&s3_0), 132);
309 assert_eq!(lens.path(), LensPath::from_vec(vec![1, 2, 0]));
310
311 let s3_1 = lens.set(s3_0, 133);
312 assert_eq!(s3_1.struct2.struct1.int32, 133);
313 assert_eq!(s3_1.struct2.struct1.int16, 116);
314
315 let s3_2 = lens.modify(s3_1, &|a| a + 1);
316 assert_eq!(s3_2.struct2.struct1.int32, 134);
317
318 let s3_3 = modify(&lens, s3_2, |a| a + 1);
319 assert_eq!(s3_3.struct2.struct1.int32, 135);
320 }
321
322 #[test]
323 fn lens_composition_should_work_with_boxed_lenses() {
324 let struct1_int32_lens: Box<dyn RefLens<Source = Struct1, Target = i32>> =
325 Box::new(Struct1Int32Lens);
326 let lens = compose_lens!(
327 Struct3Struct2Lens,
328 Box::new(Struct2Struct1Lens),
329 struct1_int32_lens
330 );
331
332 let s3_0 = make_struct3();
333 assert_eq!(*lens.get_ref(&s3_0), 132);
334
335 let s3_1 = lens.set(s3_0, 133);
336 assert_eq!(s3_1.struct2.struct1.int32, 133);
337
338 let s3_2 = lens.modify(s3_1, &|a| a + 1);
339 assert_eq!(s3_2.struct2.struct1.int32, 134);
340
341 let s3_3 = modify(&lens, s3_2, |a| a + 1);
342 assert_eq!(s3_3.struct2.struct1.int32, 135);
343 }
344
345 #[test]
346 fn value_lenses_are_generated_for_scalars_and_strings() {
347 assert_eq!(Struct1Int32Lens.get(&Struct1 { int32: 7, int16: 9 }), 7);
348 assert_eq!(
349 Struct2StringLens.get(&Struct2 {
350 int32: 0,
351 string: "hello".to_string(),
352 struct1: Struct1 { int32: 0, int16: 0 },
353 }),
354 "hello".to_string()
355 );
356 }
357
358 #[test]
359 fn a_vec_lens_should_work() {
360 let lens = vec_lens::<u32>(1);
361
362 let v0 = vec![0u32, 1, 2];
363 assert_eq!(*lens.get_ref(&v0), 1);
364 assert_eq!(lens.path(), LensPath::from_index(1));
365
366 let v1 = lens.set(v0, 42);
367 assert_eq!(v1, vec![0u32, 42, 2]);
368
369 let v2 = modify(&lens, v1, |a| a - 1);
370 assert_eq!(v2, vec![0u32, 41, 2]);
371 }
372
373 #[test]
374 #[should_panic(expected = "vector lens index 3 is out of bounds")]
375 fn a_vec_lens_panics_with_a_clear_message() {
376 let lens = vec_lens::<u32>(3);
377 let _ = lens.get_ref(&vec![0u32, 1, 2]);
378 }
379
380 #[test]
381 fn the_lens_macro_should_support_vec_indexing() {
382 let lens = crate::lens!(Struct4.inner_vec[1].int32);
383
384 let s0 = Struct4 {
385 inner_vec: vec![
386 Struct1 {
387 int32: 42,
388 int16: 73,
389 },
390 Struct1 {
391 int32: 110,
392 int16: 210,
393 },
394 ],
395 };
396 assert_eq!(*lens.get_ref(&s0), 110);
397 assert_eq!(lens.path(), LensPath::from_vec(vec![0, 1, 0]));
398
399 let s1 = lens.set(s0, 111);
400 assert_eq!(s1.inner_vec[1].int32, 111);
401
402 let s2 = modify(&lens, s1, |a| a + 1);
403 assert_eq!(s2.inner_vec[1].int32, 112);
404 }
405}