1use downcast_rs::{impl_downcast, Downcast};
2use fxhash::FxHashMap;
3use legion_core::borrow::{AtomicRefCell, Ref, RefMut};
4use legion_core::query::{Read, ReadOnly, Write};
5use std::{
6 any::TypeId,
7 marker::PhantomData,
8 ops::{Deref, DerefMut},
9};
10
11#[cfg(not(feature = "ffi"))]
12#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
14pub struct ResourceTypeId(TypeId);
15
16#[cfg(not(feature = "ffi"))]
17impl ResourceTypeId {
18 pub fn of<T: Resource>() -> Self { Self(TypeId::of::<T>()) }
20}
21
22#[cfg(feature = "ffi")]
23#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
25pub struct ResourceTypeId(TypeId, u32);
26
27#[cfg(feature = "ffi")]
28impl ResourceTypeId {
29 pub fn of<T: Resource>() -> Self { Self(TypeId::of::<T>(), 0) }
31}
32
33pub trait ResourceSet: Send + Sync {
61 type PreparedResources;
62
63 unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources;
69
70 fn fetch_mut(resources: &mut Resources) -> Self::PreparedResources {
71 unsafe { Self::fetch_unchecked(resources) }
73 }
74
75 fn fetch(resources: &Resources) -> Self::PreparedResources
76 where
77 Self: ReadOnly,
78 {
79 unsafe { Self::fetch_unchecked(resources) }
80 }
81}
82
83pub trait Resource: 'static + Downcast + Send + Sync {}
85impl<T> Resource for T where T: 'static + Send + Sync {}
86impl_downcast!(Resource);
87
88pub struct PreparedRead<T: Resource> {
96 resource: *const T,
97}
98impl<T: Resource> PreparedRead<T> {
99 pub(crate) unsafe fn new(resource: *const T) -> Self { Self { resource } }
100}
101impl<T: Resource> Deref for PreparedRead<T> {
102 type Target = T;
103
104 fn deref(&self) -> &Self::Target { unsafe { &*self.resource } }
105}
106unsafe impl<T: Resource> Send for PreparedRead<T> {}
107unsafe impl<T: Resource> Sync for PreparedRead<T> {}
108
109pub struct PreparedWrite<T: Resource> {
117 resource: *mut T,
118}
119impl<T: Resource> Deref for PreparedWrite<T> {
120 type Target = T;
121
122 fn deref(&self) -> &Self::Target { unsafe { &*self.resource } }
123}
124
125impl<T: Resource> DerefMut for PreparedWrite<T> {
126 fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.resource } }
127}
128impl<T: Resource> PreparedWrite<T> {
129 pub(crate) unsafe fn new(resource: *mut T) -> Self { Self { resource } }
130}
131unsafe impl<T: Resource> Send for PreparedWrite<T> {}
132unsafe impl<T: Resource> Sync for PreparedWrite<T> {}
133
134pub struct Fetch<'a, T: 'a + Resource> {
136 inner: Ref<'a, Box<dyn Resource>>,
137 _marker: PhantomData<T>,
138}
139impl<'a, T: Resource> Deref for Fetch<'a, T> {
140 type Target = T;
141
142 #[inline]
143 fn deref(&self) -> &Self::Target {
144 self.inner.downcast_ref::<T>().unwrap_or_else(|| {
145 panic!(
146 "Unable to downcast the resource!: {}",
147 std::any::type_name::<T>()
148 )
149 })
150 }
151}
152
153impl<'a, T: 'a + Resource + std::fmt::Debug> std::fmt::Debug for Fetch<'a, T> {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 write!(f, "{:?}", self.deref())
156 }
157}
158
159pub struct FetchMut<'a, T: Resource> {
161 inner: RefMut<'a, Box<dyn Resource>>,
162 _marker: PhantomData<T>,
163}
164impl<'a, T: 'a + Resource> Deref for FetchMut<'a, T> {
165 type Target = T;
166
167 #[inline]
168 fn deref(&self) -> &Self::Target {
169 self.inner.downcast_ref::<T>().unwrap_or_else(|| {
170 panic!(
171 "Unable to downcast the resource!: {}",
172 std::any::type_name::<T>()
173 )
174 })
175 }
176}
177
178impl<'a, T: 'a + Resource> DerefMut for FetchMut<'a, T> {
179 #[inline]
180 fn deref_mut(&mut self) -> &mut T {
181 self.inner.downcast_mut::<T>().unwrap_or_else(|| {
182 panic!(
183 "Unable to downcast the resource!: {}",
184 std::any::type_name::<T>()
185 )
186 })
187 }
188}
189
190impl<'a, T: 'a + Resource + std::fmt::Debug> std::fmt::Debug for FetchMut<'a, T> {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 write!(f, "{:?}", self.deref())
193 }
194}
195
196#[derive(Default)]
199pub struct Resources {
200 storage: FxHashMap<ResourceTypeId, AtomicRefCell<Box<dyn Resource>>>,
201}
202
203impl Resources {
204 pub fn contains<T: Resource>(&self) -> bool {
206 self.storage.contains_key(&ResourceTypeId::of::<T>())
207 }
208
209 pub fn insert<T: Resource>(&mut self, value: T) {
213 self.storage.insert(
214 ResourceTypeId::of::<T>(),
215 AtomicRefCell::new(Box::new(value)),
216 );
217 }
218
219 pub fn remove<T: Resource>(&mut self) -> Option<T> {
224 Some(
225 *self
226 .storage
227 .remove(&ResourceTypeId::of::<T>())?
228 .into_inner()
229 .downcast::<T>()
230 .ok()?,
231 )
232 }
233
234 pub fn get<T: Resource>(&self) -> Option<Fetch<'_, T>> {
236 Some(Fetch {
237 inner: self.storage.get(&ResourceTypeId::of::<T>())?.get(),
238 _marker: Default::default(),
239 })
240 }
241
242 pub fn get_mut<T: Resource>(&self) -> Option<FetchMut<'_, T>> {
244 Some(FetchMut {
245 inner: self.storage.get(&ResourceTypeId::of::<T>())?.get_mut(),
246 _marker: Default::default(),
247 })
248 }
249
250 pub fn get_or_insert_with<T: Resource, F: FnOnce() -> T>(
253 &mut self,
254 f: F,
255 ) -> Option<Fetch<'_, T>> {
256 self.get_or_insert((f)())
257 }
258
259 pub fn get_mut_or_insert_with<T: Resource, F: FnOnce() -> T>(
262 &mut self,
263 f: F,
264 ) -> Option<FetchMut<'_, T>> {
265 self.get_mut_or_insert((f)())
266 }
267
268 pub fn get_or_insert<T: Resource>(&mut self, value: T) -> Option<Fetch<'_, T>> {
271 Some(Fetch {
272 inner: self
273 .storage
274 .entry(ResourceTypeId::of::<T>())
275 .or_insert_with(|| AtomicRefCell::new(Box::new(value)))
276 .get(),
277 _marker: Default::default(),
278 })
279 }
280
281 pub fn get_mut_or_insert<T: Resource>(&mut self, value: T) -> Option<FetchMut<'_, T>> {
284 Some(FetchMut {
285 inner: self
286 .storage
287 .entry(ResourceTypeId::of::<T>())
288 .or_insert_with(|| AtomicRefCell::new(Box::new(value)))
289 .get_mut(),
290 _marker: Default::default(),
291 })
292 }
293
294 pub fn get_or_default<T: Resource + Default>(&mut self) -> Option<Fetch<'_, T>> {
299 Some(Fetch {
300 inner: self
301 .storage
302 .entry(ResourceTypeId::of::<T>())
303 .or_insert_with(|| AtomicRefCell::new(Box::new(T::default())))
304 .get(),
305 _marker: Default::default(),
306 })
307 }
308
309 pub fn get_mut_or_default<T: Resource + Default>(&mut self) -> Option<FetchMut<'_, T>> {
314 Some(FetchMut {
315 inner: self
316 .storage
317 .entry(ResourceTypeId::of::<T>())
318 .or_insert_with(|| AtomicRefCell::new(Box::new(T::default())))
319 .get_mut(),
320 _marker: Default::default(),
321 })
322 }
323
324 pub fn merge(&mut self, mut other: Resources) {
328 for resource in other.storage.drain() {
330 self.storage.entry(resource.0).or_insert(resource.1);
331 }
332 }
333}
334
335impl ResourceSet for () {
336 type PreparedResources = ();
337
338 unsafe fn fetch_unchecked(_: &Resources) {}
339}
340
341impl<T: Resource> ResourceSet for Read<T> {
342 type PreparedResources = PreparedRead<T>;
343
344 unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources {
345 let resource = resources
346 .get::<T>()
347 .unwrap_or_else(|| panic!("Failed to fetch resource!: {}", std::any::type_name::<T>()));
348 PreparedRead::new(resource.deref() as *const T)
349 }
350}
351impl<T: Resource> ResourceSet for Write<T> {
352 type PreparedResources = PreparedWrite<T>;
353
354 unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources {
355 let mut resource = resources
356 .get_mut::<T>()
357 .unwrap_or_else(|| panic!("Failed to fetch resource!: {}", std::any::type_name::<T>()));
358 PreparedWrite::new(resource.deref_mut() as *mut T)
359 }
360}
361
362macro_rules! impl_resource_tuple {
363 ( $( $ty: ident ),* ) => {
364 #[allow(unused_parens, non_snake_case)]
365 impl<$( $ty: ResourceSet ),*> ResourceSet for ($( $ty, )*)
366 {
367 type PreparedResources = ($( $ty::PreparedResources, )*);
368
369 unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources {
370 ($( $ty::fetch_unchecked(resources), )*)
371 }
372 }
373 };
374}
375impl_resource_tuple!(A);
378impl_resource_tuple!(A, B);
379impl_resource_tuple!(A, B, C);
380impl_resource_tuple!(A, B, C, D);
381impl_resource_tuple!(A, B, C, D, E);
382impl_resource_tuple!(A, B, C, D, E, F);
383impl_resource_tuple!(A, B, C, D, E, F, G);
384impl_resource_tuple!(A, B, C, D, E, F, G, H);
385impl_resource_tuple!(A, B, C, D, E, F, G, H, I);
386impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J);
387impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K);
388impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
389impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
390impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
391impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
392impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
393impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
394impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
395impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
396impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
397impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
398impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
399impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
400impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
401impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
402impl_resource_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 #[test]
409 fn simple_read_write_test() {
410 let _ = tracing_subscriber::fmt::try_init();
411
412 struct TestOne {
413 value: String,
414 }
415
416 struct TestTwo {
417 value: String,
418 }
419
420 let mut resources = Resources::default();
421 resources.insert(TestOne {
422 value: "poop".to_string(),
423 });
424
425 resources.insert(TestTwo {
426 value: "balls".to_string(),
427 });
428
429 assert_eq!(resources.get::<TestOne>().unwrap().value, "poop");
430 assert_eq!(resources.get::<TestTwo>().unwrap().value, "balls");
431
432 let owned = resources.remove::<TestTwo>();
434 assert_eq!(owned.unwrap().value, "balls")
435 }
436}