Skip to main content

accessor_pair/
lib.rs

1/// Note that `Field: 'static'` is only used for `compose` atm, so may have to be removed
2pub trait AccessorPair<Struct, Field: 'static> {
3    fn get<'a>(&self, on: &'a Struct) -> &'a Field ;
4
5    fn set<'a>(&self, on: &'a mut Struct) -> &'a mut Field ;
6
7    fn compose<'a, Next: 'static, Other: AccessorPair<Field, Next> + Clone + 'a>(&'a self, other: Other) -> (impl Fn(&Struct) -> &Next, impl Fn(&mut Struct) -> &mut Next) {
8        let other_for_get = other.clone();
9        (
10            move |s: &Struct| other_for_get.get(self.get(s)),
11            move |s: &mut Struct| other.set(self.set(s)),
12        )
13    }
14}
15
16impl<Struct, Field: 'static, Getter, Setter> AccessorPair<Struct, Field> for (Getter, Setter)
17where
18    Getter: for<'a> Fn(&'a Struct) -> &'a Field,
19    Setter: for<'a> Fn(&'a mut Struct) -> &'a mut Field,
20{
21
22    fn get<'a>(&self, on: &'a Struct) -> &'a Field {
23        self.0(on)
24    }
25
26    fn set<'a>(&self, on: &'a mut Struct) -> &'a mut Field {
27        self.1(on)
28    }
29}
30
31macro_rules! field_accessors {
32    (| $_1:ident : $struct:ty | $_2:ident . $field:ident) => {{
33        let accessor_pair: (fn(&$struct) -> &_, fn(&mut $struct) -> &mut _) =
34            (|s: &$struct| &s.$field, |s: &mut $struct| &mut s.$field);
35        accessor_pair
36    }};
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    struct PottedFern {
44        fern: Fern,
45        pot_name: String,
46    }
47
48    struct Fern {
49        species: String,
50        healthy: bool,
51    }
52
53    #[test]
54    fn test() {
55        let mut fern = Fern {
56            species: "Horsetails".to_string(),
57            healthy: true,
58        };
59
60        let species_accessor: _ = field_accessors!(|fern:Fern| fern.species);
61
62        fn test_getter<T:AccessorPair<Fern, String>>(
63            accessor: T,
64            fern: &Fern
65        ) {
66            assert_eq!(accessor.get(&fern), "Horsetails");
67        }
68        test_getter(species_accessor, &fern);
69
70        fn test_setter<T: AccessorPair<Fern, String>>(
71            accessor: T,
72            fern: &mut Fern,
73        ) {
74            *accessor.set(fern) = String::from("Equisetum");
75        }
76        test_setter(species_accessor, &mut fern);
77
78        assert_ne!(fern.species, "Horsetails");
79    }
80
81    #[test]
82    fn test_compose() {
83        let mut potted_fern = PottedFern{
84            fern: Fern { species: "Horsetails".to_string(), healthy: false },
85            pot_name: "Medium terracotta pot".to_string(),
86        };
87
88        let fern_accessor: _ = field_accessors!(|potted_fern: PottedFern| potted_fern.fern);
89        let species_accessor: _ = field_accessors!(|fern: Fern| fern.species);
90        let composed = fern_accessor.compose(species_accessor);
91
92        assert_eq!(composed.get(&potted_fern), "Horsetails");
93        *composed.set(&mut potted_fern) = "Equisetum".to_string();
94        assert_ne!(composed.get(&potted_fern), "Horsetails");
95
96    }
97}