resman/
resources.rs

1use std::{
2    any::TypeId,
3    fmt,
4    ops::{Deref, DerefMut},
5};
6
7use rt_map::{BorrowFail, Cell, RtMap};
8
9use crate::{Entry, Ref, RefMut, Resource, ResourceFetchError};
10
11/// Map from `TypeId` to type.
12#[derive(Default)]
13pub struct Resources(RtMap<TypeId, Box<dyn Resource>>);
14
15/// A [Resource] container, which provides methods to insert, access and manage
16/// the contained resources.
17///
18/// Many methods take `&self` which works because everything
19/// is stored with **interior mutability**. In case you violate
20/// the borrowing rules of Rust (multiple reads xor one write),
21/// you will get a panic.
22///
23/// # Resource Ids
24///
25/// Resources are identified by `TypeId`s, which consist of a `TypeId`.
26impl Resources {
27    /// Creates an empty `Resources` map.
28    ///
29    /// The map is initially created with a capacity of 0, so it will not
30    /// allocate until it is first inserted into.
31    ///
32    /// # Examples
33    ///
34    /// ```rust
35    /// use resman::Resources;
36    /// let mut resources = Resources::new();
37    /// ```
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Creates an empty `Resources` map with the specified capacity.
43    ///
44    /// The map will be able to hold at least capacity elements without
45    /// reallocating. If capacity is 0, the map will not allocate.
46    ///
47    /// # Examples
48    ///
49    /// ```rust
50    /// use resman::Resources;
51    /// let resources: Resources = Resources::with_capacity(10);
52    /// ```
53    pub fn with_capacity(capacity: usize) -> Self {
54        Self(RtMap::with_capacity(capacity))
55    }
56
57    /// Returns the inner [`RtMap`].
58    pub fn into_inner(self) -> RtMap<TypeId, Box<dyn Resource>> {
59        self.0
60    }
61
62    /// Returns the number of elements the map can hold without reallocating.
63    ///
64    /// This number is a lower bound; the `Resources<K, V>` might be able to
65    /// hold more, but is guaranteed to be able to hold at least this many.
66    ///
67    /// # Examples
68    ///
69    /// ```rust
70    /// use resman::Resources;
71    /// let resources: Resources = Resources::with_capacity(100);
72    /// assert!(resources.capacity() >= 100);
73    /// ```
74    pub fn capacity(&self) -> usize {
75        self.0.capacity()
76    }
77
78    /// Returns an entry for the resource with type `R`.
79    pub fn entry<R>(&mut self) -> Entry<R>
80    where
81        R: Resource,
82    {
83        Entry::new(self.0.entry(TypeId::of::<R>()))
84    }
85
86    /// Inserts a resource into the map. If the resource existed before,
87    /// it will be overwritten.
88    ///
89    /// # Examples
90    ///
91    /// Every type satisfying `Any + Send + Sync` automatically
92    /// implements `Resource`, thus can be added:
93    ///
94    /// ```rust
95    /// # #![allow(dead_code)]
96    /// struct MyRes(i32);
97    /// ```
98    ///
99    /// When you have a resource, simply insert it like this:
100    ///
101    /// ```rust
102    /// # #[derive(Debug)]
103    /// # struct MyRes(i32);
104    /// use resman::Resources;
105    ///
106    /// let mut resources = Resources::default();
107    /// resources.insert(MyRes(5));
108    /// ```
109    pub fn insert<R>(&mut self, r: R)
110    where
111        R: Resource,
112    {
113        self.0.insert(TypeId::of::<R>(), Box::new(r));
114    }
115
116    /// Inserts an already boxed resource into the map.
117    pub fn insert_raw(&mut self, type_id: TypeId, resource: Box<dyn Resource>) {
118        if type_id != Resource::type_id(&*resource) {
119            let type_name = Resource::type_name(&*resource);
120            panic!("`Resources::insert_raw` type_id does not match `{type_name:?}.type_id()`.");
121        }
122        self.0.insert(type_id, resource);
123    }
124
125    /// Removes a resource of type `R` from this container and returns its
126    /// ownership to the caller. In case there is no such resource in this,
127    /// container, `None` will be returned.
128    ///
129    /// Use this method with caution; other functions and systems might assume
130    /// this resource still exists. Thus, only use this if you're sure no
131    /// system will try to access this resource after you removed it (or else
132    /// you will get a panic).
133    ///
134    /// # Panics
135    ///
136    /// Panics if the resource doesn't exist in this container.
137    pub fn remove<R>(&mut self) -> R
138    where
139        R: Resource,
140    {
141        self.try_remove::<R>().unwrap()
142    }
143
144    /// Removes a resource of type `R` from this container and returns its
145    /// ownership to the caller. In case there is no such resource in this,
146    /// container, `None` will be returned.
147    ///
148    /// Use this method with caution; other functions and systems might assume
149    /// this resource still exists. Thus, only use this if you're sure no
150    /// system will try to access this resource after you removed it (or else
151    /// you will get a panic).
152    pub fn try_remove<R>(&mut self) -> Result<R, ResourceFetchError>
153    where
154        R: Resource,
155    {
156        self.0
157            .remove(&TypeId::of::<R>())
158            .map(|x: Box<dyn Resource>| x.downcast())
159            .map(|x: Result<Box<R>, _>| x.ok().unwrap())
160            .map(|x| *x)
161            .ok_or_else(ResourceFetchError::new::<R>)
162    }
163
164    /// Returns true if the specified resource type `R` exists in `self`.
165    pub fn contains<R>(&self) -> bool
166    where
167        R: Resource,
168    {
169        self.0.contains_key(&TypeId::of::<R>())
170    }
171
172    /// Returns the `R` resource in the resource map.
173    ///
174    /// See [`try_borrow`] for a non-panicking version of this function.
175    ///
176    /// # Panics
177    ///
178    /// Panics if the resource doesn't exist.
179    /// Panics if the resource is being accessed mutably.
180    ///
181    /// [`try_borrow`]: Self::try_borrow
182    pub fn borrow<R>(&self) -> Ref<R>
183    where
184        R: Resource,
185    {
186        self.try_borrow::<R>()
187            .unwrap_or_else(Self::borrow_panic::<R, _>)
188    }
189
190    /// Returns an immutable reference to `R` if it exists, `None` otherwise.
191    pub fn try_borrow<R>(&self) -> Result<Ref<R>, BorrowFail>
192    where
193        R: Resource,
194    {
195        self.0.try_borrow(&TypeId::of::<R>()).map(Ref::new)
196    }
197
198    /// Returns a mutable reference to `R` if it exists, `None` otherwise.
199    ///
200    /// # Panics
201    ///
202    /// Panics if the resource doesn't exist.
203    /// Panics if the resource is already accessed.
204    pub fn borrow_mut<R>(&self) -> RefMut<R>
205    where
206        R: Resource,
207    {
208        self.try_borrow_mut::<R>()
209            .unwrap_or_else(Self::borrow_panic::<R, _>)
210    }
211
212    /// Returns a mutable reference to `R` if it exists, `None` otherwise.
213    pub fn try_borrow_mut<R>(&self) -> Result<RefMut<R>, BorrowFail>
214    where
215        R: Resource,
216    {
217        self.0.try_borrow_mut(&TypeId::of::<R>()).map(RefMut::new)
218    }
219
220    /// Retrieves a resource without fetching, which is cheaper, but only
221    /// available with `&mut self`.
222    pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> {
223        self.get_resource_mut(TypeId::of::<R>())
224            .map(|res| res.downcast_mut().unwrap())
225    }
226
227    /// Retrieves a resource without fetching, which is cheaper, but only
228    /// available with `&mut self`.
229    pub fn get_resource_mut(&mut self, id: TypeId) -> Option<&mut dyn Resource> {
230        self.0.get_resource_mut(&id).map(|resource| &mut **resource)
231    }
232
233    /// Get raw access to the underlying cell.
234    pub fn get_raw(&self, id: &TypeId) -> Option<&Cell<Box<dyn Resource>>> {
235        self.0.get_raw(id)
236    }
237
238    fn borrow_panic<R, Ret>(borrow_fail: BorrowFail) -> Ret {
239        let type_name = std::any::type_name::<R>();
240        match borrow_fail {
241            BorrowFail::ValueNotFound => {
242                panic!("Expected to borrow `{type_name}`, but it does not exist.")
243            }
244            BorrowFail::BorrowConflictImm => panic!(
245                "Expected to borrow `{type_name}` immutably, but it was already borrowed mutably."
246            ),
247            BorrowFail::BorrowConflictMut => panic!(
248                "Expected to borrow `{type_name}` mutably, but it was already borrowed mutably."
249            ),
250        }
251    }
252
253    /// Merges the other `Resources` map over this one.
254    pub fn merge(&mut self, other: Resources) {
255        self.0.extend(other.into_inner().into_inner())
256    }
257}
258
259#[cfg(not(feature = "debug"))]
260impl fmt::Debug for Resources {
261    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262        let mut debug_map = f.debug_map();
263
264        self.0.keys().for_each(|type_id| {
265            let resource = &*self.0.borrow(type_id);
266            let type_name = resource.as_ref().type_name();
267
268            // At runtime, we are unable to determine if the resource is `Debug`.
269            debug_map.entry(&type_name, &"..");
270        });
271
272        debug_map.finish()
273    }
274}
275
276#[cfg(feature = "debug")]
277impl fmt::Debug for Resources {
278    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
279        let mut debug_map = f.debug_map();
280
281        self.0.keys().for_each(|type_id| {
282            let resource = &*self.0.borrow(type_id);
283            let type_name = resource.as_ref().type_name();
284
285            debug_map.entry(&type_name, resource);
286        });
287
288        debug_map.finish()
289    }
290}
291
292impl Deref for Resources {
293    type Target = RtMap<TypeId, Box<dyn Resource>>;
294
295    fn deref(&self) -> &Self::Target {
296        &self.0
297    }
298}
299
300impl DerefMut for Resources {
301    fn deref_mut(&mut self) -> &mut Self::Target {
302        &mut self.0
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use std::any::TypeId;
309
310    use super::Resources;
311    use crate::{BorrowFail, ResourceFetchError};
312
313    #[test]
314    fn entry_or_insert_inserts_value() {
315        #[derive(Debug, PartialEq)]
316        struct A(usize);
317
318        let mut resources = Resources::new();
319        let mut a_ref = resources.entry::<A>().or_insert(A(1));
320
321        assert_eq!(&A(1), &*a_ref);
322
323        *a_ref = A(2);
324
325        drop(a_ref);
326
327        assert_eq!(&A(2), &*resources.borrow::<A>());
328    }
329
330    #[cfg(not(feature = "debug"))]
331    #[test]
332    fn debug_uses_placeholder_for_values() {
333        let mut resources = Resources::new();
334
335        resources.insert(1u32);
336        resources.insert(2u64);
337
338        let resources_dbg = format!("{:?}", resources);
339        assert!(
340            resources_dbg.contains(r#"u32: "..""#),
341            r#"Expected `{}` to contain `u32: ".."`"#,
342            resources_dbg
343        );
344        assert!(
345            resources_dbg.contains(r#"u64: "..""#),
346            r#"Expected `{}` to contain `u64: ".."`"#,
347            resources_dbg
348        );
349    }
350
351    #[cfg(feature = "debug")]
352    #[test]
353    fn debug_uses_debug_implementation_for_values() {
354        let mut resources = Resources::new();
355
356        resources.insert(1u32);
357        resources.insert(2u64);
358
359        let resources_dbg = format!("{:?}", resources);
360        assert!(
361            resources_dbg.contains(r#"u32: 1"#),
362            r#"Expected `{}` to contain `u32: 1`"#,
363            resources_dbg
364        );
365        assert!(
366            resources_dbg.contains(r#"u64: 2"#),
367            r#"Expected `{}` to contain `u64: 2`"#,
368            resources_dbg
369        );
370    }
371
372    #[test]
373    fn with_capacity_reserves_enough_capacity() {
374        let map = Resources::with_capacity(100);
375        assert!(map.capacity() >= 100);
376    }
377
378    #[test]
379    fn into_inner() {
380        let mut resources = Resources::default();
381        resources.insert(Res);
382
383        let rt_map = resources.into_inner();
384
385        assert!(rt_map.contains_key(&TypeId::of::<Res>()));
386    }
387
388    #[test]
389    fn insert() {
390        #[cfg_attr(feature = "debug", derive(Debug))]
391        struct Foo;
392
393        let mut resources = Resources::default();
394        resources.insert(Res);
395
396        assert!(resources.contains::<Res>());
397        assert!(!resources.contains::<Foo>());
398    }
399
400    #[test]
401    fn insert_raw() {
402        #[cfg_attr(feature = "debug", derive(Debug))]
403        struct Foo;
404
405        let mut resources = Resources::default();
406        resources.insert_raw(TypeId::of::<Res>(), Box::new(Res));
407
408        assert!(resources.contains::<Res>());
409        assert!(!resources.contains::<Foo>());
410    }
411
412    #[test]
413    #[should_panic(
414        expected = "`Resources::insert_raw` type_id does not match `resman::resources::tests::Res.type_id()`."
415    )]
416    fn insert_raw_panics_when_boxed_resource_does_not_match_key() {
417        #[cfg_attr(feature = "debug", derive(Debug))]
418        struct Foo;
419
420        let mut resources = Resources::default();
421        resources.insert_raw(TypeId::of::<Foo>(), Box::new(Res));
422
423        assert!(resources.contains::<Res>());
424    }
425
426    #[test]
427    #[should_panic(
428        expected = "Expected to borrow `resman::resources::tests::Res` mutably, but it was already borrowed mutably."
429    )]
430    fn read_write_fails() {
431        let mut resources = Resources::default();
432        resources.insert(Res);
433
434        let _read = resources.borrow::<Res>();
435        let _write = resources.borrow_mut::<Res>();
436    }
437
438    #[test]
439    #[should_panic(expected = "but it was already borrowed mutably")]
440    fn write_read_fails() {
441        let mut resources = Resources::default();
442        resources.insert(Res);
443
444        let _write = resources.borrow_mut::<Res>();
445        let _read = resources.borrow::<Res>();
446    }
447
448    #[test]
449    fn remove_insert() {
450        let mut resources = Resources::default();
451        resources.insert(Res);
452
453        assert!(resources.contains::<Res>());
454
455        resources.remove::<Res>();
456
457        assert!(!resources.contains::<Res>());
458
459        resources.insert(Res);
460
461        assert!(resources.contains::<Res>());
462    }
463
464    #[test]
465    fn try_remove() {
466        let mut resources = Resources::default();
467        resources.insert(Res);
468
469        assert!(resources.contains::<Res>());
470
471        assert_eq!(Ok(Res), resources.try_remove::<Res>());
472        assert!(matches!(
473            resources.try_remove::<Res>(),
474            Err(ResourceFetchError {
475                resource_name_short,
476                resource_name_full,
477            }) if resource_name_short == "Res"
478            && resource_name_full == "resman::resources::tests::Res"
479        ));
480    }
481
482    #[test]
483    #[should_panic(
484        expected = "Expected to borrow `resman::resources::tests::Res`, but it does not exist."
485    )]
486    fn borrow_before_insert_panics() {
487        let resources = Resources::default();
488
489        resources.borrow::<Res>();
490    }
491
492    #[test]
493    #[should_panic(
494        expected = "Expected to borrow `resman::resources::tests::Res`, but it does not exist."
495    )]
496    fn borrow_mut_before_insert_panics() {
497        let resources = Resources::default();
498
499        resources.borrow_mut::<Res>();
500    }
501
502    #[test]
503    fn borrow_mut_try_borrow_returns_err() {
504        let mut resources = Resources::default();
505        resources.insert(Res);
506
507        let _res = resources.borrow_mut::<Res>();
508
509        assert_eq!(
510            Err(BorrowFail::BorrowConflictImm),
511            resources.try_borrow::<Res>()
512        );
513    }
514
515    #[test]
516    fn borrow_try_borrow_mut_returns_err() {
517        let mut resources = Resources::default();
518        resources.insert(Res);
519
520        let _res = resources.borrow::<Res>();
521
522        assert_eq!(
523            Err(BorrowFail::BorrowConflictMut),
524            resources.try_borrow_mut::<Res>()
525        );
526    }
527
528    #[test]
529    fn borrow_mut_borrow_mut_returns_err() {
530        let mut resources = Resources::default();
531        resources.insert(Res);
532
533        let _res = resources.borrow_mut::<Res>();
534
535        assert_eq!(
536            Err(BorrowFail::BorrowConflictMut),
537            resources.try_borrow_mut::<Res>()
538        );
539    }
540
541    #[test]
542    fn get_mut_returns_ok() {
543        let mut resources = Resources::default();
544        resources.insert(Res);
545
546        let _res = resources.get_mut::<Res>();
547
548        assert!(resources.try_borrow_mut::<Res>().is_ok());
549    }
550
551    #[test]
552    fn get_resource_mut_returns_some() {
553        let mut resources = Resources::default();
554        resources.insert(Res);
555
556        assert!(resources.get_resource_mut(TypeId::of::<Res>()).is_some());
557    }
558
559    #[test]
560    fn get_raw_returns_some() {
561        let mut resources = Resources::default();
562        resources.insert(Res);
563
564        assert!(resources.get_raw(&TypeId::of::<Res>()).is_some());
565    }
566
567    #[test]
568    fn merge() {
569        let mut resources_0 = Resources::default();
570        resources_0.insert(1u8);
571        resources_0.insert(2u16);
572
573        let mut resources_1 = Resources::default();
574        resources_1.insert(3u8);
575        resources_1.insert(4u32);
576
577        resources_0.merge(resources_1);
578
579        assert_eq!(3u8, *resources_0.borrow::<u8>());
580        assert_eq!(4u32, *resources_0.borrow::<u32>());
581    }
582
583    #[derive(Debug, Default, PartialEq)]
584    struct Res;
585}