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}