Skip to main content

pebble/ecs/
resources.rs

1use std::{
2    any::{Any, TypeId},
3    cell::{Ref, RefCell, RefMut},
4    collections::HashMap,
5};
6
7use crate::ecs::system_param::SystemParam;
8
9/// Singleton storage for app-wide resources, keyed by type. One value per
10/// type, borrow-checked at runtime via `RefCell` rather than the compiler —
11/// [`Read`]/[`Write`] are the system-facing way to borrow from this.
12#[derive(Default)]
13pub struct Resources {
14    map: HashMap<TypeId, RefCell<Box<dyn Any>>>,
15}
16
17impl Resources {
18    fn cell<T: 'static>(&self) -> &RefCell<Box<dyn Any>> {
19        self.map
20            .get(&TypeId::of::<T>())
21            .unwrap_or_else(|| panic!("Resource not found: {}", std::any::type_name::<T>()))
22    }
23
24    /// Inserts a value, replacing any existing one of the same type.
25    pub fn insert<T: 'static>(&mut self, value: T) {
26        self.map
27            .insert(TypeId::of::<T>(), RefCell::new(Box::new(value)));
28    }
29
30    /// Borrows `T` immutably. Panics if it isn't present.
31    pub fn get<T: 'static>(&self) -> Ref<'_, T> {
32        Ref::map(self.cell::<T>().borrow(), |b| b.downcast_ref::<T>().unwrap())
33    }
34
35    /// Borrows `T` mutably. Panics if it isn't present.
36    pub fn get_mut<T: 'static>(&self) -> RefMut<'_, T> {
37        RefMut::map(self.cell::<T>().borrow_mut(), |b| b.downcast_mut::<T>().unwrap())
38    }
39
40    /// Removes and returns `T`, if present.
41    pub fn remove<T: 'static>(&mut self) -> Option<T> {
42        self.map
43            .remove(&TypeId::of::<T>())
44            .map(|cell| *cell.into_inner().downcast::<T>().unwrap())
45    }
46
47    /// Whether a value of type `T` is currently stored.
48    pub fn contains<T: 'static>(&self) -> bool {
49        self.map.contains_key(&TypeId::of::<T>())
50    }
51}
52
53/// An immutable resource borrow, fetched automatically as a system
54/// parameter. Panics at fetch time if `T` isn't in [`Resources`] — use
55/// `Option<Read<T>>` for a resource that might not exist.
56pub struct Read<'a, T: 'static> {
57    pub(crate) inner: Ref<'a, T>,
58}
59
60impl<'a, T> std::ops::Deref for Read<'a, T> {
61    type Target = T;
62
63    fn deref(&self) -> &Self::Target {
64        &self.inner
65    }
66}
67
68/// A mutable resource borrow, fetched automatically as a system parameter.
69/// Panics at fetch time if `T` isn't in [`Resources`] — use
70/// `Option<Write<T>>` for a resource that might not exist.
71pub struct Write<'a, T: 'static> {
72    pub(crate) inner: RefMut<'a, T>,
73}
74
75impl<'a, T> std::ops::Deref for Write<'a, T> {
76    type Target = T;
77
78    fn deref(&self) -> &Self::Target {
79        &self.inner
80    }
81}
82
83impl<'a, T> std::ops::DerefMut for Write<'a, T> {
84    fn deref_mut(&mut self) -> &mut Self::Target {
85        &mut self.inner
86    }
87}
88
89impl<T: 'static> SystemParam for Read<'_, T> {
90    type Item<'a> = Read<'a, T>;
91    type State = ();
92
93    fn fetch<'a>(
94        _world: &'a hecs::World,
95        resources: &'a Resources,
96        _state: &'a mut Self::State,
97    ) -> Self::Item<'a> {
98        Read {
99            inner: resources.get::<T>(),
100        }
101    }
102}
103
104impl<T: 'static> SystemParam for Write<'_, T> {
105    type Item<'a> = Write<'a, T>;
106    type State = ();
107
108    fn fetch<'a>(
109        _world: &'a hecs::World,
110        resources: &'a Resources,
111        _state: &'a mut Self::State,
112    ) -> Self::Item<'a> {
113        Write {
114            inner: resources.get_mut::<T>(),
115        }
116    }
117}
118
119impl<T> SystemParam for Option<Read<'static, T>>
120where
121    T: 'static + Sync + Send,
122{
123    type Item<'a> = Option<Read<'a, T>>;
124    type State = ();
125
126    fn fetch<'a>(
127        world: &'a hecs::World,
128        resources: &'a Resources,
129        state: &'a mut Self::State,
130    ) -> Self::Item<'a> {
131        resources.contains::<T>().then(|| Read::fetch(world, resources, state))
132    }
133}
134
135impl<T> SystemParam for Option<Write<'static, T>>
136where
137    T: 'static + Sync + Send,
138{
139    type Item<'a> = Option<Write<'a, T>>;
140    type State = ();
141
142    fn fetch<'a>(
143        world: &'a hecs::World,
144        resources: &'a Resources,
145        state: &'a mut Self::State,
146    ) -> Self::Item<'a> {
147        resources.contains::<T>().then(|| Write::fetch(world, resources, state))
148    }
149}