Skip to main content

karpal_optics/
getter.rs

1use crate::optic::Optic;
2
3/// A read-only optic that extracts a single focus from a source.
4///
5/// This is the "getter" component of a lens, without any modification capability.
6pub struct Getter<S, A> {
7    get: fn(&S) -> A,
8}
9
10impl<S, A> Optic for Getter<S, A> {}
11
12impl<S, A> Getter<S, A> {
13    pub fn new(get: fn(&S) -> A) -> Self {
14        Self { get }
15    }
16
17    pub fn get(&self, s: &S) -> A {
18        (self.get)(s)
19    }
20
21    /// Compose with another getter for deeper access.
22    pub fn then<B>(self, inner: Getter<A, B>) -> ComposedGetter<S, B>
23    where
24        S: 'static,
25        A: 'static,
26        B: 'static,
27    {
28        let outer_get = self.get;
29        let inner_get = inner.get;
30        ComposedGetter {
31            get: Box::new(move |s| inner_get(&outer_get(s))),
32        }
33    }
34}
35
36/// A composed getter using boxed closures (from composition or conversion).
37pub struct ComposedGetter<S, A> {
38    get: Box<dyn Fn(&S) -> A>,
39}
40
41impl<S, A> Optic for ComposedGetter<S, A> {}
42
43impl<S, A> ComposedGetter<S, A> {
44    pub fn get(&self, s: &S) -> A {
45        (self.get)(s)
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[derive(Clone)]
54    struct Point {
55        x: f64,
56        y: f64,
57    }
58
59    #[test]
60    fn getter_get() {
61        let getter = Getter::new(|p: &Point| p.x);
62        let p = Point { x: 1.0, y: 2.0 };
63        assert!((getter.get(&p) - 1.0).abs() < f64::EPSILON);
64    }
65
66    #[test]
67    fn getter_composition() {
68        #[derive(Clone)]
69        struct Line {
70            start: Point,
71        }
72        let line_start = Getter::new(|l: &Line| l.start.clone());
73        let point_x = Getter::new(|p: &Point| p.x);
74        let composed = line_start.then(point_x);
75        let line = Line {
76            start: Point { x: 3.0, y: 4.0 },
77        };
78        assert!((composed.get(&line) - 3.0).abs() < f64::EPSILON);
79    }
80
81    #[test]
82    fn getter_from_lens() {
83        use crate::lens::Lens;
84        let lens = Lens::new(|p: &Point| p.x, |p: Point, x| Point { x, ..p });
85        let getter = lens.to_getter();
86        let p = Point { x: 5.0, y: 6.0 };
87        assert!((getter.get(&p) - 5.0).abs() < f64::EPSILON);
88    }
89
90    #[test]
91    fn getter_from_iso() {
92        use crate::iso::Iso;
93        let iso = Iso::new(|n: &i32| *n as f64, |f: f64| f as i32);
94        let getter = iso.to_getter();
95        assert!((getter.get(&42) - 42.0).abs() < f64::EPSILON);
96    }
97}