resources/map.rs
1use downcast_rs::{impl_downcast, Downcast};
2use fxhash::FxHashMap;
3use parking_lot::RwLock;
4use std::any::TypeId;
5
6use crate::{
7 entry::Entry,
8 error::{CantGetResource, NoSuchResource},
9 refs::{Ref, RefMut},
10};
11
12#[cfg(feature = "fetch")]
13use crate::fetch::{CantFetch, Fetch};
14
15/// Types that can be stored in [`Resources`], automatically implemented for all applicable.
16///
17/// [`Resources`]: struct.Resources.html
18pub trait Resource: Downcast + Send + Sync + 'static {}
19
20impl<T> Resource for T where T: Send + Sync + 'static {}
21
22impl_downcast!(Resource);
23
24/// A [`Resource`] container, for storing at most one resource of each specific type.
25///
26/// Internally, this is a [`FxHashMap`] of [`TypeId`] to [`RwLock`]. None of the methods are
27/// blocking, however: accessing a resource in a way that would break borrow rules will
28/// return the [`InvalidBorrow`] error instead.
29///
30/// [`Resource`]: trait.Resource.html
31/// [`FxHashMap`]: ../fxhash/type.FxHashMap.html
32/// [`TypeId`]: https://doc.rust-lang.org/std/any/struct.TypeId.html
33/// [`RwLock`]: ../parking_lot/type.RwLock.html
34/// [`InvalidBorrow`]: enum.InvalidBorrow.html
35#[derive(Default)]
36pub struct Resources {
37 resources: FxHashMap<TypeId, RwLock<Box<dyn Resource>>>,
38}
39
40fn downcast_resource<T: Resource>(resource: Box<dyn Resource>) -> T {
41 *resource
42 .downcast::<T>()
43 .unwrap_or_else(|_| panic!("downcasting resources should always succeed"))
44}
45
46impl Resources {
47 /// Creates an empty container. Functionally identical to [`::default()`].
48 ///
49 /// [`default`]: #method.default
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 /// Returns `true` if a resource of type `T` exists in the container.
55 pub fn contains<T: Resource>(&self) -> bool {
56 self.resources.contains_key(&TypeId::of::<T>())
57 }
58
59 /// Inserts the given resource of type `T` into the container.
60 ///
61 /// If a resource of this type was already present,
62 /// it will be updated, and the original returned.
63 pub fn insert<T: Resource>(&mut self, resource: T) -> Option<T> {
64 self.resources
65 .insert(TypeId::of::<T>(), RwLock::new(Box::new(resource)))
66 .map(|resource| downcast_resource(resource.into_inner()))
67 }
68
69 /// Removes the resource of type `T` from the container.
70 ///
71 /// If a resource of this type was present in the container, it will be returned.
72 pub fn remove<T: Resource>(&mut self) -> Option<T> {
73 self.resources
74 .remove(&TypeId::of::<T>())
75 .map(|resource| downcast_resource(resource.into_inner()))
76 }
77
78 /// Gets the type `T`'s corresponding entry for in-place manipulation.
79 pub fn entry<T: Resource>(&mut self) -> Entry<T> {
80 Entry::from_hash_map_entry(self.resources.entry(TypeId::of::<T>()))
81 }
82
83 /// Returns a reference to the stored resource of type `T`.
84 ///
85 /// If such a resource is currently accessed mutably elsewhere,
86 /// or is not present in the container, returns the appropriate error.
87 pub fn get<T: Resource>(&self) -> Result<Ref<T>, CantGetResource> {
88 self.resources
89 .get(&TypeId::of::<T>())
90 .ok_or_else(|| NoSuchResource.into())
91 .and_then(|lock| Ref::from_lock(lock).map_err(|error| error.into()))
92 }
93
94 /// Returns a mutable reference to the stored resource of type `T`.
95 ///
96 /// If such a resource is currently accessed immutably or mutably elsewhere,
97 /// or is not present in the container, returns the appropriate error.
98 pub fn get_mut<T: Resource>(&self) -> Result<RefMut<T>, CantGetResource> {
99 self.resources
100 .get(&TypeId::of::<T>())
101 .ok_or_else(|| NoSuchResource.into())
102 .and_then(|lock| RefMut::from_lock(lock).map_err(|error| error.into()))
103 }
104
105 /// Retrieves up to 16 resources of any combination of mutability.
106 ///
107 /// The generic parameter accepts a single one or any tuple (up to 16)
108 /// of immutable or mutable references of types that are to be retrieved.
109 ///
110 /// # Example
111 /// ```rust
112 /// # use resources::Resources;
113 /// let mut resources = Resources::new();
114 /// assert!(resources.insert(0f32).is_none());
115 /// assert!(resources.insert(1u32).is_none());
116 /// assert!(resources.insert(2usize).is_none());
117 /// {
118 /// let res_f32 = resources.fetch::<&f32>().unwrap();
119 /// assert_eq!(*res_f32, 0f32);
120 /// }
121 /// {
122 /// let (mut res_f32, res_u32) = resources.fetch::<(&mut f32, &u32)>().unwrap();
123 /// assert_eq!(*res_u32, 1u32);
124 /// *res_f32 += *res_u32 as f32;
125 /// }
126 /// {
127 /// let (res_f32, res_usize) = resources.fetch::<(&f32, &usize)>().unwrap();
128 /// assert_eq!(*res_f32, 1f32);
129 /// assert_eq!(*res_usize, 2usize);
130 /// assert!(resources.fetch::<&mut f32>().is_err()); // f32 is already borrowed.
131 /// }
132 /// assert!(resources.fetch::<&bool>().is_err());// There is no bool in the container.
133 /// ```
134 #[cfg(feature = "fetch")]
135 pub fn fetch<R>(&self) -> Result<<R as Fetch>::Refs, CantFetch>
136 where
137 for<'a> R: Fetch<'a>,
138 {
139 R::fetch(self)
140 }
141}