rs_ecs/
resources.rs

1use std::any::{type_name, Any, TypeId};
2use std::cell::{Ref, RefCell, RefMut};
3use std::collections::hash_map::{Entry, HashMap};
4use std::hash::{BuildHasherDefault, Hasher};
5use std::ops::{Deref, DerefMut};
6
7/// A type map for holding resources.
8///
9/// Resources replace global variables and my be accessed by systems that know their type.
10///
11/// # Examples
12///
13/// ```
14/// # use rs_ecs::*;
15/// struct WrapperType(u32);
16///
17/// let mut resources = Resources::new();
18///
19/// // Insert multiple resources
20/// resources.insert(42_u32);
21/// resources.insert(WrapperType(23));
22///
23/// // Borrow a resource immutably
24/// let wrapped_res = resources.get::<WrapperType>();
25///
26/// // Borrow a resource mutably
27/// let mut u32_res = resources.get_mut::<u32>();
28/// *u32_res += 1;
29/// ```
30pub struct Resources(TypeIdMap<RefCell<Box<dyn Any>>>);
31
32impl Default for Resources {
33    /// Create an empty resources map.
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl Resources {
40    /// Create an empty resources map.
41    pub fn new() -> Self {
42        Self(Default::default())
43    }
44}
45
46impl Resources {
47    /// Insert a resource.
48    ///
49    /// # Panics
50    ///
51    /// Panics if a resource of the same type is already present.
52    pub fn insert<R>(&mut self, res: R)
53    where
54        R: 'static,
55    {
56        match self.0.entry(TypeId::of::<R>()) {
57            Entry::Vacant(entry) => entry.insert(RefCell::new(Box::new(res))),
58            Entry::Occupied(_) => panic!("Resource {} already present", type_name::<R>()),
59        };
60    }
61
62    /// Drop all resources.
63    pub fn clear(&mut self) {
64        self.0.clear();
65    }
66}
67
68impl Resources {
69    /// Borrow a resource immutably
70    ///
71    /// # Panics
72    ///
73    /// Panics if the resource is not present.
74    pub fn get<R>(&self) -> Res<'_, R>
75    where
76        R: 'static,
77    {
78        let ref_ = self
79            .0
80            .get(&TypeId::of::<R>())
81            .unwrap_or_else(|| panic!("Resource {} not present", type_name::<R>()))
82            .try_borrow()
83            .unwrap_or_else(|_err| panic!("Resource {} already borrwed", type_name::<R>()));
84
85        Res(Ref::map(ref_, |ref_| unsafe {
86            &*(ref_.deref() as *const dyn Any as *const R)
87        }))
88    }
89
90    /// Borrow a resource mutably
91    ///
92    /// # Panics
93    ///
94    /// Panics if the resource is not present.
95    pub fn get_mut<R>(&self) -> ResMut<'_, R>
96    where
97        R: 'static,
98    {
99        let ref_ = self
100            .0
101            .get(&TypeId::of::<R>())
102            .unwrap_or_else(|| panic!("Resource {} not present", type_name::<R>()))
103            .try_borrow_mut()
104            .unwrap_or_else(|_err| panic!("Resource {} already borrwed", type_name::<R>()));
105
106        ResMut(RefMut::map(ref_, |ref_| unsafe {
107            &mut *(ref_.deref_mut() as *mut dyn Any as *mut R)
108        }))
109    }
110}
111
112/// An immutable borrow of a resource.
113pub struct Res<'a, R>(Ref<'a, R>);
114
115impl<R> Deref for Res<'_, R> {
116    type Target = R;
117
118    fn deref(&self) -> &R {
119        self.0.deref()
120    }
121}
122
123/// A mutable borrow of a resource.
124pub struct ResMut<'a, R>(RefMut<'a, R>);
125
126impl<R> Deref for ResMut<'_, R> {
127    type Target = R;
128
129    fn deref(&self) -> &R {
130        self.0.deref()
131    }
132}
133
134impl<R> DerefMut for ResMut<'_, R> {
135    fn deref_mut(&mut self) -> &mut R {
136        self.0.deref_mut()
137    }
138}
139
140pub type TypeIdMap<V> = HashMap<TypeId, V, BuildHasherDefault<TypeIdHasher>>;
141
142#[derive(Default)]
143pub struct TypeIdHasher(u64);
144
145impl Hasher for TypeIdHasher {
146    fn write_u64(&mut self, val: u64) {
147        self.0 = val;
148    }
149
150    fn write(&mut self, _val: &[u8]) {
151        unreachable!();
152    }
153
154    fn finish(&self) -> u64 {
155        self.0
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn insert_then_get() {
165        let mut resources = Resources::new();
166
167        resources.insert(42_u64);
168
169        let res = resources.get::<u64>();
170        assert_eq!(*res, 42);
171    }
172
173    #[test]
174    fn get_mut_then_get() {
175        let mut resources = Resources::new();
176
177        resources.insert(42_u64);
178
179        {
180            let mut res = resources.get_mut::<u64>();
181            *res = 23;
182        }
183
184        let res = resources.get::<u64>();
185        assert_eq!(*res, 23);
186    }
187
188    #[test]
189    #[should_panic]
190    fn insert_does_not_replace() {
191        let mut resources = Resources::new();
192
193        resources.insert(23_i32);
194        resources.insert(42_i32);
195    }
196
197    #[test]
198    fn borrows_can_be_shared() {
199        let mut resources = Resources::new();
200
201        resources.insert(23_i32);
202
203        let _res = resources.get::<i32>();
204        let _res = resources.get::<i32>();
205    }
206
207    #[test]
208    #[should_panic]
209    fn mutable_borrows_are_exclusive() {
210        let mut resources = Resources::new();
211
212        resources.insert(23_i32);
213
214        let _res = resources.get_mut::<i32>();
215        let _res = resources.get_mut::<i32>();
216    }
217}