1use std::any::TypeId;
2use std::marker::PhantomData;
3use std::sync::{RwLockReadGuard, RwLockWriteGuard};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ResourceFetchError {
7 NotFound(TypeId),
8 BorrowConflict(TypeId),
9}
10
11pub struct ResourceReadGuard<'a, T> {
12 pub(crate) guard: RwLockReadGuard<'a, Box<dyn std::any::Any + Send + Sync>>,
13 pub(crate) _marker: PhantomData<T>,
14}
15
16impl<'a, T: 'static> std::ops::Deref for ResourceReadGuard<'a, T> {
17 type Target = T;
18 fn deref(&self) -> &Self::Target {
19 self.guard.downcast_ref::<T>().unwrap()
20 }
21}
22
23pub struct ResourceWriteGuard<'a, T> {
24 pub(crate) guard: RwLockWriteGuard<'a, Box<dyn std::any::Any + Send + Sync>>,
25 pub(crate) _marker: PhantomData<T>,
26}
27
28impl<'a, T: 'static> std::ops::Deref for ResourceWriteGuard<'a, T> {
29 type Target = T;
30 fn deref(&self) -> &Self::Target {
31 self.guard.downcast_ref::<T>().unwrap()
32 }
33}
34
35impl<'a, T: 'static> std::ops::DerefMut for ResourceWriteGuard<'a, T> {
36 fn deref_mut(&mut self) -> &mut Self::Target {
37 self.guard.downcast_mut::<T>().unwrap()
38 }
39}
40
41#[cfg(test)]
44mod tests {
45 use crate::impl_component;
46 use crate::Entity;
47 use crate::World;
48
49 #[derive(Debug, Clone, PartialEq)]
51 struct Position {
52 x: f32,
53 y: f32,
54 }
55
56 #[derive(Debug, Clone, PartialEq)]
57 struct Health(u32);
58
59 impl_component!(Position, Health);
60
61 #[test]
62 fn test_spawn_and_alive_count() {
63 let mut world = World::new();
64 let e1 = world.spawn();
65 let e2 = world.spawn();
66 let e3 = world.spawn();
67 assert_eq!(world.entity_count(), 3);
68 assert!(world.is_alive(e1));
69 assert!(world.is_alive(e2));
70 assert!(world.is_alive(e3));
71 }
72
73 #[test]
74 fn test_despawn_removes_components() {
75 let mut world = World::new();
76 let e1 = world.spawn();
77 world.add_component(e1, Position { x: 1.0, y: 2.0 });
78 world.add_component(e1, Health(100));
79
80 assert!(world.borrow::<Position>().get(e1.id()).is_some());
81 assert!(world.borrow::<Health>().get(e1.id()).is_some());
82
83 world.despawn(e1);
84
85 assert!(!world.is_alive(e1));
86 assert!(world.borrow::<Position>().get(e1.id()).is_none());
87 assert!(world.borrow::<Health>().get(e1.id()).is_none());
88 }
89
90 #[test]
91 fn test_despawn_only_touches_relevant_storages() {
92 let mut world = World::new();
93 let e1 = world.spawn();
94 let e2 = world.spawn();
95
96 world.add_component(e1, Position { x: 0.0, y: 0.0 });
98 world.add_component(e2, Position { x: 1.0, y: 1.0 });
99 world.add_component(e2, Health(50));
100
101 world.despawn(e1);
103
104 assert!(world.borrow::<Position>().get(e2.id()).is_some());
105 assert!(world.borrow::<Health>().get(e2.id()).is_some());
106 assert_eq!(world.entity_count(), 1);
107 }
108
109 #[test]
110 fn test_iter_alive_entities_zero_allocation() {
111 let mut world = World::new();
112 let _e1 = world.spawn();
113 let e2 = world.spawn();
114 let _e3 = world.spawn();
115
116 world.despawn(e2);
117
118 let alive: Vec<Entity> = world.iter_alive_entities();
120 assert_eq!(alive.len(), 2);
121 assert!(alive.iter().all(|e: &Entity| e.id() != e2.id()));
122 }
123
124 #[test]
125 fn test_entity_id_reuse_after_despawn() {
126 let mut world = World::new();
127 let e1 = world.spawn();
128 let old_id = e1.id();
129 let old_gen = e1.generation();
130
131 world.despawn(e1);
132
133 let e_new = world.spawn();
134 assert_eq!(e_new.id(), old_id);
136 assert_eq!(e_new.generation(), old_gen + 1);
137
138 assert!(!world.is_alive(e1));
140 assert!(world.is_alive(e_new));
141 }
142
143 #[test]
144 fn test_add_component_overwrites() {
145 let mut world = World::new();
146 let e = world.spawn();
147 world.add_component(e, Health(100));
148 world.add_component(e, Health(50)); let hp = world.borrow::<Health>();
151 assert_eq!(hp.get(e.id()).unwrap().0, 50);
152 }
153
154 #[test]
156 fn test_double_add_component_despawn_safe() {
157 let mut world = World::new();
158 let e = world.spawn();
159 let id = e.id();
160 world.add_component(e, Health(100));
161 world.add_component(e, Health(50));
162
163 world.despawn(e);
164
165 assert!(!world.is_alive(e));
166 assert!(world.borrow::<Health>().get(id).is_none());
167
168 let e2 = world.spawn();
170 assert_eq!(e2.id(), id);
171 assert!(world.borrow::<Health>().get(e2.id()).is_none());
172 }
173
174 #[test]
175 fn test_component_registration_metadata() {
176 let mut world = World::new();
177 assert_eq!(world.registered_component_count(), 0);
178 assert!(!world.is_component_registered::<Position>());
179 assert!(!world.is_component_registered::<Health>());
180
181 let e = world.spawn();
182 world.add_component(e, Position { x: 1.0, y: 2.0 });
183 assert!(world.is_component_registered::<Position>());
184 assert_eq!(world.registered_component_count(), 1);
185
186 world.add_component(e, Health(100));
187 assert!(world.is_component_registered::<Health>());
188 assert_eq!(world.registered_component_count(), 2);
189
190 world.remove_component::<Health>(e);
192 assert!(world.is_component_registered::<Health>());
193 assert_eq!(world.registered_component_count(), 2);
194 }
195
196 #[derive(Debug, Clone, PartialEq)]
197 struct HookTracker(u32);
198 impl_component!(HookTracker);
199
200 #[test]
201 fn test_component_hooks() {
202 use std::sync::atomic::{AtomicUsize, Ordering};
203 static ADD_COUNT: AtomicUsize = AtomicUsize::new(0);
204 static SET_COUNT: AtomicUsize = AtomicUsize::new(0);
205 static REMOVE_COUNT: AtomicUsize = AtomicUsize::new(0);
206
207 ADD_COUNT.store(0, Ordering::SeqCst);
208 SET_COUNT.store(0, Ordering::SeqCst);
209 REMOVE_COUNT.store(0, Ordering::SeqCst);
210
211 let mut world = World::new();
212
213 world.register_on_add::<HookTracker>(Box::new(|_, _| {
214 ADD_COUNT.fetch_add(1, Ordering::SeqCst);
215 }));
216 world.register_on_set::<HookTracker>(Box::new(|_, _| {
217 SET_COUNT.fetch_add(1, Ordering::SeqCst);
218 }));
219 world.register_on_remove::<HookTracker>(Box::new(|_, _| {
220 REMOVE_COUNT.fetch_add(1, Ordering::SeqCst);
221 }));
222
223 let e1 = world.spawn();
224
225 world.add_component(e1, HookTracker(1));
227 assert_eq!(ADD_COUNT.load(Ordering::SeqCst), 1);
228 assert_eq!(SET_COUNT.load(Ordering::SeqCst), 1);
229 assert_eq!(REMOVE_COUNT.load(Ordering::SeqCst), 0);
230
231 world.add_component(e1, HookTracker(2));
233 assert_eq!(ADD_COUNT.load(Ordering::SeqCst), 1);
234 assert_eq!(SET_COUNT.load(Ordering::SeqCst), 2);
235 assert_eq!(REMOVE_COUNT.load(Ordering::SeqCst), 0);
236
237 world.remove_component::<HookTracker>(e1);
239 assert_eq!(ADD_COUNT.load(Ordering::SeqCst), 1);
240 assert_eq!(SET_COUNT.load(Ordering::SeqCst), 2);
241 assert_eq!(REMOVE_COUNT.load(Ordering::SeqCst), 1);
242
243 world.add_component(e1, HookTracker(3));
245 assert_eq!(ADD_COUNT.load(Ordering::SeqCst), 2); assert_eq!(SET_COUNT.load(Ordering::SeqCst), 3);
247
248 world.despawn(e1);
249 assert_eq!(REMOVE_COUNT.load(Ordering::SeqCst), 2); }
251
252 #[test]
253 fn test_world_compaction() {
254 let mut world = World::new();
255
256 for _ in 0..100 {
258 let e = world.spawn();
259 world.add_component(e, Position { x: 0.0, y: 0.0 });
260 world.add_component(e, Health(10));
261 }
262
263 assert_eq!(world.archetype_index.archetype_count(), 3); let all_entities = world.iter_alive_entities();
266
267 for e in all_entities.iter().take(50) {
270 world.remove_component::<Health>(*e);
271 }
272
273 for e in all_entities.iter().skip(50) {
275 world.despawn(*e);
276 }
277
278 assert_eq!(world.archetype_index.archetypes[2].len(), 0);
281
282 world.compact();
284
285 assert_eq!(world.archetype_index.archetype_count(), 2);
287
288 let pos_view = world.borrow::<Position>();
290 let mut count = 0;
291 for e in world.iter_alive_entities() {
292 let eid: u32 = e.id();
293 assert!(pos_view.get(eid).is_some());
294 count += 1;
295 }
296 assert_eq!(count, 50);
297 }
298}