maplibre/tcs/
resources.rs

1use std::{any, any::TypeId, cell::UnsafeCell, collections::HashMap};
2
3use downcast_rs::{impl_downcast, Downcast};
4
5use crate::tcs::{EphemeralQueryState, GlobalQueryState, QueryState};
6
7pub trait Resource: Downcast + 'static {}
8impl_downcast!(Resource);
9
10impl<T> Resource for T where T: 'static {}
11
12#[derive(Default)]
13pub struct Resources {
14    resources: Vec<UnsafeCell<Box<dyn Resource>>>,
15    index: HashMap<TypeId, usize>,
16}
17
18impl Resources {
19    pub fn init<R: Resource + Default>(&mut self) {
20        self.insert(R::default());
21    }
22
23    pub fn get_or_init_mut<R: Resource + Default>(&mut self) -> &mut R {
24        if self.exists::<R>() {
25            self.get_mut::<R>()
26                .expect("unable get get just initialized resource")
27        } else {
28            self.init::<R>();
29            self.get_mut()
30                .expect("unable get get just initialized resource")
31        }
32    }
33
34    pub fn insert<R: Resource>(&mut self, resource: R) {
35        let index = self.resources.len();
36        self.resources.push(UnsafeCell::new(Box::new(resource)));
37        self.index.insert(TypeId::of::<R>(), index);
38    }
39
40    pub fn exists<R: Resource>(&self) -> bool {
41        self.index.contains_key(&TypeId::of::<R>())
42    }
43
44    pub fn get<R: Resource>(&self) -> Option<&R> {
45        if let Some(index) = self.index.get(&TypeId::of::<R>()) {
46            unsafe {
47                return Some(
48                    self.resources[*index]
49                        // FIXME tcs: Is this safe? We cast directly to & instead of &mut
50                        .get()
51                        .as_ref()
52                        .unwrap()
53                        .downcast_ref()
54                        .expect("inserted resource has wrong TypeId"),
55                );
56            }
57        }
58        None
59    }
60
61    pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> {
62        if let Some(index) = self.index.get(&TypeId::of::<R>()) {
63            return Some(
64                self.resources[*index]
65                    .get_mut()
66                    .downcast_mut()
67                    .expect("inserted resource has wrong TypeId"),
68            );
69        }
70        None
71    }
72
73    pub fn query<Q: ResourceQuery>(&self) -> Option<Q::Item<'_>> {
74        let mut global_state = GlobalQueryState::default();
75        let state = <Q::State<'_> as QueryState>::create(&mut global_state);
76        Q::query(self, state)
77    }
78
79    pub fn query_mut<Q: ResourceQueryMut>(&mut self) -> Option<Q::MutItem<'_>> {
80        let mut global_state = GlobalQueryState::default();
81        let state = <Q::State<'_> as QueryState>::create(&mut global_state);
82        Q::query_mut(self, state)
83    }
84}
85
86// ResourceQuery
87
88pub trait ResourceQuery {
89    type Item<'r>;
90
91    type State<'s>: QueryState<'s>;
92
93    fn query<'r, 's>(resources: &'r Resources, state: Self::State<'s>) -> Option<Self::Item<'r>>;
94}
95
96impl<'a, R: Resource> ResourceQuery for &'a R {
97    type Item<'r> = &'r R;
98    type State<'s> = EphemeralQueryState<'s>;
99
100    fn query<'r, 's>(resources: &'r Resources, _state: Self::State<'s>) -> Option<Self::Item<'r>> {
101        resources.get::<R>()
102    }
103}
104
105// ResourceQueryMut
106
107pub trait ResourceQueryMut {
108    type MutItem<'r>;
109
110    type State<'s>: QueryState<'s>;
111
112    fn query_mut<'r, 's>(
113        resources: &'r mut Resources,
114        state: Self::State<'s>,
115    ) -> Option<Self::MutItem<'r>>;
116}
117
118impl<'a, R: Resource> ResourceQueryMut for &'a R {
119    type MutItem<'r> = &'r R;
120    type State<'s> = EphemeralQueryState<'s>;
121
122    fn query_mut<'r, 's>(
123        resources: &'r mut Resources,
124        state: Self::State<'s>,
125    ) -> Option<Self::MutItem<'r>> {
126        <&R as ResourceQuery>::query(resources, state)
127    }
128}
129
130impl<'a, R: Resource> ResourceQueryMut for &'a mut R {
131    type MutItem<'r> = &'r mut R;
132    type State<'s> = EphemeralQueryState<'s>;
133
134    fn query_mut<'r, 's>(
135        resources: &'r mut Resources,
136        _state: Self::State<'s>,
137    ) -> Option<Self::MutItem<'r>> {
138        resources.get_mut::<R>()
139    }
140}
141
142// ResourceQueryUnsafe
143
144pub trait ResourceQueryUnsafe: ResourceQueryMut {
145    unsafe fn query_unsafe<'r, 's>(
146        resources: &'r Resources,
147        state: Self::State<'s>,
148    ) -> Option<Self::MutItem<'r>>;
149}
150
151impl<'a, R: Resource> ResourceQueryUnsafe for &'a R {
152    unsafe fn query_unsafe<'r, 's>(
153        resources: &'r Resources,
154        state: Self::State<'s>,
155    ) -> Option<Self::MutItem<'r>> {
156        <&R as ResourceQuery>::query(resources, state)
157    }
158}
159
160impl<'a, R: Resource> ResourceQueryUnsafe for &'a mut R {
161    /// SAFETY: Safe if tiles is borrowed mutably.
162    // FIXME tcs: check if really safe
163    unsafe fn query_unsafe<'r, 's>(
164        resources: &'r Resources,
165        state: Self::State<'s>,
166    ) -> Option<Self::MutItem<'r>> {
167        let id = TypeId::of::<R>();
168        let borrowed = &mut state.state.mutably_borrowed;
169
170        if borrowed.contains(&id) {
171            panic!(
172                "tried to borrow an {} more than once mutably",
173                any::type_name::<R>()
174            )
175        }
176
177        borrowed.insert(id);
178
179        if let Some(index) = resources.index.get(&TypeId::of::<R>()) {
180            return Some(
181                resources.resources[*index]
182                    .get()
183                    .as_mut()
184                    .unwrap()
185                    .downcast_mut()
186                    .expect("inserted resource has wrong TypeId"),
187            );
188        }
189
190        None
191    }
192}
193
194// Lift to tuples
195
196macro_rules! impl_resource_query {
197    ($($param: ident),*) => {
198        impl<$($param: ResourceQuery),*> ResourceQuery for ($($param,)*) {
199            type Item<'r> = ($($param::Item<'r>,)*);
200            type State<'s> = EphemeralQueryState<'s>;
201
202            fn query<'r, 's>(resources: &'r Resources, mut state: Self::State<'s>) -> Option<Self::Item<'r>> {
203                Some(
204                    (
205                        $($param::query(resources, state.clone_to::<$param::State<'_>>())?,)*
206                    )
207                )
208            }
209        }
210
211        impl<$($param: ResourceQueryMut + ResourceQueryUnsafe + 'static),*> ResourceQueryMut for ($($param,)*)
212        {
213            type MutItem<'r> = ($($param::MutItem<'r>,)*);
214            type State<'s> = EphemeralQueryState<'s>;
215
216            fn query_mut<'r, 's>(
217                resources: &'r mut Resources,
218                mut state: Self::State<'s>,
219            ) -> Option<Self::MutItem<'r>> {
220                unsafe {
221                    Some(
222                        (
223                            $(<$param as ResourceQueryUnsafe>::query_unsafe(resources, state.clone_to::<$param::State<'_>>())?,)*
224                        )
225                    )
226                }
227            }
228        }
229    };
230}
231
232impl_resource_query!(R1);
233impl_resource_query!(R1, R2);
234impl_resource_query!(R1, R2, R3);
235impl_resource_query!(R1, R2, R3, R4);
236impl_resource_query!(R1, R2, R3, R4, R5);
237impl_resource_query!(R1, R2, R3, R4, R5, R6);