1use std::any::{Any, TypeId};
7use std::collections::HashMap;
8
9pub trait Resource: Send + Sync + 'static {}
13
14impl<T: Send + Sync + 'static> Resource for T {}
16
17struct ResourceEntry {
19 data: Box<dyn Any + Send + Sync>,
20 type_name: &'static str,
21}
22
23#[derive(Default)]
48pub struct Resources {
49 storage: HashMap<TypeId, ResourceEntry>,
50}
51
52impl Resources {
53 pub fn new() -> Self {
55 Self {
56 storage: HashMap::new(),
57 }
58 }
59
60 pub fn insert<R: Resource>(&mut self, resource: R) -> Option<R> {
64 let type_id = TypeId::of::<R>();
65 let type_name = std::any::type_name::<R>();
66
67 let entry = ResourceEntry {
68 data: Box::new(resource),
69 type_name,
70 };
71
72 self.storage
73 .insert(type_id, entry)
74 .and_then(|old| old.data.downcast::<R>().ok().map(|b| *b))
75 }
76
77 pub fn get<R: Resource>(&self) -> Option<&R> {
79 let type_id = TypeId::of::<R>();
80 self.storage
81 .get(&type_id)
82 .and_then(|entry| entry.data.downcast_ref())
83 }
84
85 pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> {
87 let type_id = TypeId::of::<R>();
88 self.storage
89 .get_mut(&type_id)
90 .and_then(|entry| entry.data.downcast_mut())
91 }
92
93 pub fn remove<R: Resource>(&mut self) -> Option<R> {
95 let type_id = TypeId::of::<R>();
96 self.storage
97 .remove(&type_id)
98 .and_then(|entry| entry.data.downcast::<R>().ok().map(|b| *b))
99 }
100
101 pub fn contains<R: Resource>(&self) -> bool {
103 let type_id = TypeId::of::<R>();
104 self.storage.contains_key(&type_id)
105 }
106
107 pub fn len(&self) -> usize {
109 self.storage.len()
110 }
111
112 pub fn is_empty(&self) -> bool {
114 self.storage.is_empty()
115 }
116
117 pub fn clear(&mut self) {
119 self.storage.clear();
120 }
121
122 pub fn get_or_insert_with<R: Resource>(&mut self, f: impl FnOnce() -> R) -> &mut R {
124 let type_id = TypeId::of::<R>();
125
126 if !self.storage.contains_key(&type_id) {
127 self.insert(f());
128 }
129
130 self.get_mut::<R>().unwrap()
131 }
132
133 pub fn get_or_default<R: Resource + Default>(&mut self) -> &mut R {
135 self.get_or_insert_with(R::default)
136 }
137
138 pub fn type_names(&self) -> impl Iterator<Item = &'static str> + '_ {
140 self.storage.values().map(|entry| entry.type_name)
141 }
142}
143
144impl std::fmt::Debug for Resources {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 f.debug_struct("Resources")
147 .field("count", &self.storage.len())
148 .field("types", &self.type_names().collect::<Vec<_>>())
149 .finish()
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_insert_and_get() {
159 let mut resources = Resources::new();
160 resources.insert(42i32);
161 resources.insert("hello".to_string());
162
163 assert_eq!(*resources.get::<i32>().unwrap(), 42);
164 assert_eq!(resources.get::<String>().unwrap(), "hello");
165 }
166
167 #[test]
168 fn test_get_mut() {
169 let mut resources = Resources::new();
170 resources.insert(vec![1, 2, 3]);
171
172 resources.get_mut::<Vec<i32>>().unwrap().push(4);
173 assert_eq!(resources.get::<Vec<i32>>().unwrap(), &vec![1, 2, 3, 4]);
174 }
175
176 #[test]
177 fn test_replace() {
178 let mut resources = Resources::new();
179 resources.insert(10i32);
180 let old = resources.insert(20i32);
181
182 assert_eq!(old, Some(10));
183 assert_eq!(*resources.get::<i32>().unwrap(), 20);
184 }
185
186 #[test]
187 fn test_remove() {
188 let mut resources = Resources::new();
189 resources.insert(42i32);
190
191 let removed = resources.remove::<i32>();
192 assert_eq!(removed, Some(42));
193 assert!(resources.get::<i32>().is_none());
194 }
195
196 #[test]
197 fn test_contains() {
198 let mut resources = Resources::new();
199 assert!(!resources.contains::<i32>());
200
201 resources.insert(42i32);
202 assert!(resources.contains::<i32>());
203 }
204
205 #[test]
206 fn test_get_or_default() {
207 let mut resources = Resources::new();
208
209 let val = resources.get_or_default::<Vec<i32>>();
210 val.push(1);
211
212 assert_eq!(resources.get::<Vec<i32>>().unwrap(), &vec![1]);
213 }
214
215 #[test]
216 fn test_get_or_insert_with() {
217 let mut resources = Resources::new();
218 let mut called = false;
219
220 resources.get_or_insert_with(|| {
221 called = true;
222 42i32
223 });
224 assert!(called);
225
226 called = false;
227 resources.get_or_insert_with(|| {
228 called = true;
229 100i32
230 });
231 assert!(!called); assert_eq!(*resources.get::<i32>().unwrap(), 42);
233 }
234}