1pub 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}