Skip to main content

keepcalm/
projection.rs

1/// Given a type, projects a reference into that type as another type.
2pub trait ProjectR<A: ?Sized, B: ?Sized>: Send + Sync {
3    fn project<'a>(&self, a: &'a A) -> &'a B;
4}
5
6impl<A: ?Sized, B: ?Sized, T> ProjectR<A, B> for T
7where
8    Self: Send + Sync,
9    T: for<'a> Fn(&'a A) -> &'a B,
10{
11    fn project<'a>(&self, a: &'a A) -> &'a B {
12        self(a)
13    }
14}
15
16/// Given a type, projects a mutable reference into that type as another type.
17pub trait ProjectW<A: ?Sized, B: ?Sized>: Send + Sync {
18    fn project_mut<'a>(&self, a: &'a mut A) -> &'a mut B;
19}
20
21impl<A: ?Sized, B: ?Sized, T> ProjectW<A, B> for T
22where
23    Self: Send + Sync,
24    T: for<'a> Fn(&'a mut A) -> &'a mut B,
25{
26    fn project_mut<'a>(&self, a: &'a mut A) -> &'a mut B {
27        self(a)
28    }
29}
30
31/// Stores a read/write projection.
32pub struct ProjectorRW<A: ?Sized, B: ?Sized> {
33    pub(crate) ro: Box<dyn ProjectR<A, B> + Send + Sync>,
34    pub(crate) rw: Box<dyn ProjectW<A, B> + Send + Sync>,
35}
36
37impl<A: ?Sized, B: ?Sized> ProjectorRW<A, B> {
38    pub fn new(ro: impl ProjectR<A, B> + 'static, rw: impl ProjectW<A, B> + 'static) -> Self {
39        Self {
40            ro: Box::new(ro),
41            rw: Box::new(rw),
42        }
43    }
44
45    pub fn project<'a>(&self, a: &'a A) -> &'a B {
46        self.ro.project(a)
47    }
48
49    pub fn project_mut<'a>(&self, a: &'a mut A) -> &'a mut B {
50        self.rw.project_mut(a)
51    }
52}
53
54/// Stores a read projection.
55pub struct Projector<A: ?Sized, B: ?Sized> {
56    ro: Box<dyn ProjectR<A, B> + Send + Sync>,
57}
58
59impl<A: ?Sized, B: ?Sized> Projector<A, B> {
60    pub fn new(ro: impl ProjectR<A, B> + 'static) -> Self {
61        Self { ro: Box::new(ro) }
62    }
63
64    pub fn project<'a>(&self, a: &'a A) -> &'a B {
65        self.ro.project(a)
66    }
67}
68
69/// Extract the [`Projector`] from a [`ProjectorRW`].
70impl<A: ?Sized, B: ?Sized> From<ProjectorRW<A, B>> for Projector<A, B> {
71    fn from(value: ProjectorRW<A, B>) -> Self {
72        Self { ro: value.ro }
73    }
74}
75
76/// Project part of a type as another type.
77///
78/// Given a reference to a type `A`, we can project that into a reference of type `B`, where the reference for `B` comes
79/// from somewhere within `A` (for example: an arbitrarily nested field, part of a tuple, or part of a slice).
80///
81/// ```rust
82/// # use keepcalm::*;
83/// // Creates two projections for each field of a tuple:
84/// let projection0 = project!(x: (i32, i32), x.0);
85/// let projection1 = project!(x: (i32, i32), x.1);
86///
87/// assert_eq!(1, *projection0.project(&(1, 2)));
88/// assert_eq!(2, *projection1.project(&(1, 2)));
89/// ```
90#[macro_export]
91macro_rules! project {
92    ($x:ident : $type:ty, $expr:expr) => {{
93        // We just need something with a type
94        let $x: [$type; 0] = [];
95        fn make_projection<A, B>(
96            _: &[A; 0],
97            a: impl (Fn(&A) -> &B) + Send + Sync + 'static,
98            b: impl (Fn(&mut A) -> &mut B) + Send + Sync + 'static,
99        ) -> $crate::ProjectorRW<A, B> {
100            $crate::ProjectorRW::new(a, b)
101        }
102        make_projection(&$x, |$x: _| &$expr, |$x: _| &mut $expr)
103    }};
104}
105
106/// Projects a type as another type.
107///
108/// This performs a cast from one type to another, and is useful for creating generic shared objects based on traits rather than
109/// concrete types, and without having to make methods generic.
110///
111/// ```rust
112/// # use keepcalm::*;
113/// let projection = project_cast!(x: [i32; 3] => dyn std::ops::IndexMut<usize, Output = i32>);
114///
115/// let mut x = [1, 2, 3];
116/// projection.project_mut(&mut x)[0] += 10;
117/// assert_eq!(projection.project(&x)[0], 11);
118/// ```
119#[macro_export]
120macro_rules! project_cast {
121    ($x:ident : $type:ty => $type2:ty) => {{
122        // We just need something with a type
123        fn make_projection<A: ?Sized, B: ?Sized>(
124            a: impl (Fn(&A) -> &B) + Send + Sync + 'static,
125            b: impl (Fn(&mut A) -> &mut B) + Send + Sync + 'static,
126        ) -> $crate::ProjectorRW<A, B> {
127            $crate::ProjectorRW::new(a, b)
128        }
129        make_projection(
130            |$x: &$type| $x as &$type2,
131            |$x: &mut $type| $x as &mut $type2,
132        )
133    }};
134}
135
136/// A trait that allows a type to be cast to another, generically unsized type.
137pub trait Castable<T: 'static>
138where
139    T: ?Sized,
140{
141    fn cast<'a>(&'a self) -> &'a T
142    where
143        Self: 'a;
144    fn cast_mut<'a>(&'a mut self) -> &'a mut T
145    where
146        Self: 'a;
147}
148
149macro_rules! impl_castable_trait {
150    ($trait:path) => {
151        impl<T_> Castable<dyn $trait + 'static> for T_
152        where
153            T_: $trait + 'static,
154        {
155            fn cast<'a>(&'a self) -> &'a (dyn $trait + 'static)
156            where
157                Self: 'a,
158            {
159                self
160            }
161            fn cast_mut<'a>(&'a mut self) -> &'a mut (dyn $trait + 'static)
162            where
163                Self: 'a,
164            {
165                self
166            }
167        }
168    };
169}
170
171// Implement for common std traits
172impl_castable_trait!(std::any::Any);
173impl_castable_trait!(std::fmt::Debug);
174impl_castable_trait!(std::fmt::Display);
175
176macro_rules! impl_castable_ref_trait {
177    ($type:ident, $trait:path) => {
178        impl<T_, $type: 'static> Castable<dyn $trait + 'static> for T_
179        where
180            T_: $trait + 'static,
181            $type: ?Sized,
182        {
183            fn cast<'a>(&'a self) -> &'a (dyn $trait + 'static)
184            where
185                Self: 'a,
186            {
187                self
188            }
189            fn cast_mut<'a>(&'a mut self) -> &'a mut (dyn $trait + 'static)
190            where
191                Self: 'a,
192            {
193                self
194            }
195        }
196    };
197}
198
199// Implement for common std traits involving references
200impl_castable_ref_trait!(T, std::convert::AsRef<T>);
201impl_castable_ref_trait!(T, std::convert::AsMut<T>);
202impl_castable_ref_trait!(T, std::ops::Deref<Target = T>);
203impl_castable_ref_trait!(T, std::ops::DerefMut<Target = T>);
204impl_castable_ref_trait!(T, std::borrow::Borrow<T>);
205impl_castable_ref_trait!(T, std::borrow::BorrowMut<T>);
206
207impl<U: 'static, const N: usize> Castable<[U]> for [U; N] {
208    fn cast<'a>(&'a self) -> &'a [U]
209    where
210        Self: 'a,
211    {
212        self
213    }
214    fn cast_mut<'a>(&'a mut self) -> &'a mut [U]
215    where
216        Self: 'a,
217    {
218        self
219    }
220}
221
222impl<T: ?Sized, U: ?Sized + 'static> ProjectR<T, U> for ()
223where
224    T: Castable<U>,
225{
226    fn project<'a>(&self, t: &'a T) -> &'a U {
227        t.cast()
228    }
229}
230
231impl<T: ?Sized, U: ?Sized + 'static> ProjectW<T, U> for ()
232where
233    T: Castable<U>,
234{
235    fn project_mut<'a>(&self, t: &'a mut T) -> &'a mut U {
236        t.cast_mut()
237    }
238}
239
240#[cfg(test)]
241mod test {
242    #[test]
243    fn test_projection() {
244        let x = (1, 2);
245        let projection1 = project!(x: (i32, i32), x.0);
246        let projection2 = project!(x: (i32, i32), x.1);
247        assert_eq!(1, *projection1.ro.project(&x));
248        assert_eq!(2, *projection2.ro.project(&x));
249    }
250
251    #[test]
252    fn test_projection_cast() {
253        trait AsRefMut<T: ?Sized>: AsRef<T> + AsMut<T> {}
254        impl AsRefMut<str> for String {}
255
256        let mut x: String = "123".into();
257        let projection = project_cast!(x: String => (dyn AsRefMut<str>));
258        assert_eq!(projection.project(&x).as_ref(), "123");
259        assert_eq!(projection.project_mut(&mut x).as_mut(), "123");
260    }
261
262    #[test]
263    fn test_projection_cast_array() {
264        let projection = project_cast!(x: [i32; 3] => dyn std::ops::IndexMut<usize, Output = i32>);
265        let mut x = [1, 2, 3];
266        projection.project_mut(&mut x)[0] = 11;
267    }
268}