async_ecs/resource/
resources.rs

1use std::marker::PhantomData;
2use std::ops::{Deref, DerefMut};
3
4use hashbrown::HashMap;
5use mopa::Any;
6
7pub use super::cell::Cell;
8
9use super::{
10    cell::{Ref as CellRef, RefMut as CellRefMut},
11    entry::Entry,
12    Resource, ResourceId,
13};
14
15#[derive(Default)]
16pub struct Resources {
17    resources: HashMap<ResourceId, Cell<Box<dyn Resource>>>,
18}
19
20pub struct Ref<'a, R: 'a> {
21    inner: CellRef<'a, dyn Resource>,
22    phantom: PhantomData<&'a R>,
23}
24
25pub struct RefMut<'a, R: 'a> {
26    inner: CellRefMut<'a, dyn Resource>,
27    phantom: PhantomData<&'a R>,
28}
29
30macro_rules! fetch_panic {
31    () => {{
32        panic!(
33            "\
34            Tried to fetch resource from the resources map, but the resource does not exist.\n\
35\n\
36            Resource: `{resource_name_full}`\n\
37\n\
38            You may ensure the resource exists!\
39            ",
40            resource_name_full = std::any::type_name::<R>(),
41        )
42    }};
43}
44
45/// A [Resource] container, which provides methods to insert, access and manage
46/// the contained resources.
47///
48/// Many methods take `&self` which works because everything
49/// is stored with **interior mutability**. In case you violate
50/// the borrowing rules of Rust (multiple reads xor one write),
51/// you will get a panic.
52///
53/// # Resource Ids
54///
55/// Resources are identified by `ResourceId`s, which consist of a `TypeId`.
56impl Resources {
57    /// Returns an entry for the resource with type `R`.
58    pub fn entry<R>(&mut self) -> Entry<R>
59    where
60        R: Resource,
61    {
62        Entry::new(self.resources.entry(ResourceId::new::<R>()))
63    }
64
65    /// Inserts a resource into this container. If the resource existed before,
66    /// it will be overwritten.
67    ///
68    /// # Examples
69    ///
70    /// Every type satisfying `Any + Send + Sync` automatically
71    /// implements `Resource`, thus can be added:
72    ///
73    /// ```rust
74    /// # #![allow(dead_code)]
75    /// struct MyRes(i32);
76    /// ```
77    ///
78    /// When you have a resource, simply insert it like this:
79    ///
80    /// ```rust
81    /// # struct MyRes(i32);
82    /// use async_ecs::World;
83    ///
84    /// let mut world = World::default();
85    /// world.insert(MyRes(5));
86    /// ```
87    pub fn insert<R>(&mut self, r: R)
88    where
89        R: Resource,
90    {
91        self.resources
92            .insert(ResourceId::new::<R>(), Cell::new(Box::new(r)));
93    }
94
95    /// Removes a resource of type `R` from this container and returns its
96    /// ownership to the caller. In case there is no such resource in this,
97    /// container, `None` will be returned.
98    ///
99    /// Use this method with caution; other functions and systems might assume
100    /// this resource still exists. Thus, only use this if you're sure no
101    /// system will try to access this resource after you removed it (or else
102    /// you will get a panic).
103    pub fn remove<R>(&mut self) -> Option<R>
104    where
105        R: Resource,
106    {
107        self.resources
108            .remove(&ResourceId::new::<R>())
109            .map(Cell::into_inner)
110            .map(|x: Box<dyn Resource>| x.downcast())
111            .map(|x: Result<Box<R>, _>| x.ok().unwrap())
112            .map(|x| *x)
113    }
114
115    /// Returns true if the specified resource type `R` exists in `self`.
116    pub fn contains<R>(&self) -> bool
117    where
118        R: Resource,
119    {
120        self.resources.contains_key(&ResourceId::new::<R>())
121    }
122
123    /// Fetches the resource with the specified type `T` or panics if it doesn't
124    /// exist.
125    ///
126    /// # Panics
127    ///
128    /// Panics if the resource doesn't exist.
129    /// Panics if the resource is being accessed mutably.
130    pub fn borrow<R>(&self) -> Ref<R>
131    where
132        R: Resource,
133    {
134        self.try_borrow().unwrap_or_else(|| fetch_panic!())
135    }
136
137    /// Like `fetch`, but returns an `Option` instead of inserting a default
138    /// value in case the resource does not exist.
139    pub fn try_borrow<R>(&self) -> Option<Ref<R>>
140    where
141        R: Resource,
142    {
143        self.resources.get(&ResourceId::new::<R>()).map(|r| Ref {
144            inner: CellRef::map(r.borrow(), Box::as_ref),
145            phantom: PhantomData,
146        })
147    }
148
149    /// Fetches the resource with the specified type `T` mutably.
150    ///
151    /// Please see `fetch` for details.
152    ///
153    /// # Panics
154    ///
155    /// Panics if the resource doesn't exist.
156    /// Panics if the resource is already being accessed.
157    pub fn borrow_mut<R>(&self) -> RefMut<R>
158    where
159        R: Resource,
160    {
161        self.try_borrow_mut().unwrap_or_else(|| fetch_panic!())
162    }
163
164    /// Like `fetch_mut`, but returns an `Option` instead of inserting a default
165    /// value in case the resource does not exist.
166    pub fn try_borrow_mut<R>(&self) -> Option<RefMut<R>>
167    where
168        R: Resource,
169    {
170        self.resources.get(&ResourceId::new::<R>()).map(|r| RefMut {
171            inner: r.borrow_mut().map(Box::as_mut),
172            phantom: PhantomData,
173        })
174    }
175
176    /// Retrieves a resource without fetching, which is cheaper, but only
177    /// available with `&mut self`.
178    pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> {
179        self.get_resource_mut(ResourceId::new::<R>())
180            .map(|res| res.downcast_mut().unwrap())
181    }
182
183    /// Retrieves a resource without fetching, which is cheaper, but only
184    /// available with `&mut self`.
185    pub fn get_resource_mut(&mut self, id: ResourceId) -> Option<&mut dyn Resource> {
186        self.resources
187            .get_mut(&id)
188            .map(Cell::get_mut)
189            .map(Box::as_mut)
190    }
191
192    /// Get raw access to the underlying cell.
193    pub fn get_raw(&self, id: &ResourceId) -> Option<&Cell<Box<dyn Resource>>> {
194        self.resources.get(id)
195    }
196}
197
198/* Resource */
199
200impl<T> Resource for T where T: Any + Send + Sync {}
201
202mod __resource_mopafy_scope {
203    #![allow(clippy::all)]
204
205    use mopa::mopafy;
206
207    use super::super::Resource;
208
209    mopafy!(Resource);
210}
211
212/* Ref */
213
214impl<'a, R> Ref<'a, R> {
215    pub fn new(inner: CellRef<'a, dyn Resource>) -> Self {
216        Self {
217            inner,
218            phantom: PhantomData,
219        }
220    }
221}
222
223impl<'a, R> Deref for Ref<'a, R>
224where
225    R: Resource,
226{
227    type Target = R;
228
229    fn deref(&self) -> &R {
230        unsafe { self.inner.downcast_ref_unchecked() }
231    }
232}
233
234impl<'a, R> Clone for Ref<'a, R> {
235    fn clone(&self) -> Self {
236        Ref {
237            inner: self.inner.clone(),
238            phantom: PhantomData,
239        }
240    }
241}
242
243/* RefMut */
244
245impl<'a, R> RefMut<'a, R> {
246    pub fn new(inner: CellRefMut<'a, dyn Resource>) -> Self {
247        Self {
248            inner,
249            phantom: PhantomData,
250        }
251    }
252}
253
254impl<'a, R> Deref for RefMut<'a, R>
255where
256    R: Resource,
257{
258    type Target = R;
259
260    fn deref(&self) -> &R {
261        unsafe { self.inner.downcast_ref_unchecked() }
262    }
263}
264
265impl<'a, R> DerefMut for RefMut<'a, R>
266where
267    R: Resource,
268{
269    fn deref_mut(&mut self) -> &mut R {
270        unsafe { self.inner.downcast_mut_unchecked() }
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277
278    #[derive(Default)]
279    struct Res;
280
281    #[test]
282    fn insert() {
283        struct Foo;
284
285        let mut resources = Resources::default();
286        resources.insert(Res);
287
288        assert!(resources.contains::<Res>());
289        assert!(!resources.contains::<Foo>());
290    }
291
292    #[test]
293    #[should_panic(expected = "but it was already borrowed")]
294    fn read_write_fails() {
295        let mut resources = Resources::default();
296        resources.insert(Res);
297
298        let _read = resources.borrow::<Res>();
299        let _write = resources.borrow_mut::<Res>();
300    }
301
302    #[test]
303    #[should_panic(expected = "but it was already borrowed mutably")]
304    fn write_read_fails() {
305        let mut resources = Resources::default();
306        resources.insert(Res);
307
308        let _write = resources.borrow_mut::<Res>();
309        let _read = resources.borrow::<Res>();
310    }
311
312    #[test]
313    fn remove_insert() {
314        let mut resources = Resources::default();
315        resources.insert(Res);
316
317        assert!(resources.contains::<Res>());
318
319        resources.remove::<Res>().unwrap();
320
321        assert!(!resources.contains::<Res>());
322
323        resources.insert(Res);
324
325        assert!(resources.contains::<Res>());
326    }
327}