Skip to main content

goud_engine/ecs/
storage.rs

1//! Component storage traits and implementations.
2//!
3//! This module defines the [`ComponentStorage`] trait, which provides an abstract
4//! interface for component storage backends. The primary implementation is
5//! [`SparseSet`], but the trait allows for alternative storage strategies in the future.
6//!
7//! # Design Philosophy
8//!
9//! Component storage is separated from the component type itself to allow:
10//!
11//! - **Flexibility**: Different components can use different storage strategies
12//! - **Optimization**: Future storage options (table-based, chunk-based, etc.)
13//! - **Abstraction**: The ECS world can work with type-erased storage
14//!
15//! # Thread Safety
16//!
17//! All storage implementations must be `Send + Sync` to enable parallel system
18//! execution. The storage itself is not internally synchronized - concurrent
19//! access must be managed at a higher level (e.g., via `RwLock` or scheduler).
20//!
21//! # Example
22//!
23//! ```
24//! use goud_engine::ecs::{Entity, Component, SparseSet, ComponentStorage};
25//!
26//! #[derive(Debug, Clone, Copy, PartialEq)]
27//! struct Position { x: f32, y: f32 }
28//! impl Component for Position {}
29//!
30//! // SparseSet implements ComponentStorage
31//! let mut storage: SparseSet<Position> = SparseSet::new();
32//!
33//! let entity = Entity::new(0, 1);
34//! storage.insert(entity, Position { x: 10.0, y: 20.0 });
35//!
36//! assert!(storage.contains(entity));
37//! assert_eq!(storage.get(entity), Some(&Position { x: 10.0, y: 20.0 }));
38//! ```
39
40use super::{Component, Entity, SparseSet};
41
42/// Trait for component storage backends.
43///
44/// `ComponentStorage` defines the interface for storing components indexed by entity.
45/// Any type implementing this trait can be used as a storage backend for components
46/// in the ECS world.
47///
48/// # Type Parameters
49///
50/// The associated type `Item` specifies what component type this storage holds.
51/// It must implement [`Component`] to ensure thread safety requirements are met.
52///
53/// # Thread Safety
54///
55/// Implementations must be `Send + Sync` to allow the ECS world to be shared
56/// across threads. This is enforced by the trait bounds.
57///
58/// # Object Safety
59///
60/// This trait is **not** object-safe due to the associated type. For type-erased
61/// storage, use [`AnyComponentStorage`] which provides a runtime interface.
62///
63/// # Example Implementation
64///
65/// ```
66/// use goud_engine::ecs::{Entity, Component, ComponentStorage, SparseSet};
67///
68/// // Define a custom storage (wrapping SparseSet for this example)
69/// struct MyStorage<T: Component> {
70///     inner: SparseSet<T>,
71/// }
72///
73/// impl<T: Component> ComponentStorage for MyStorage<T> {
74///     type Item = T;
75///
76///     fn insert(&mut self, entity: Entity, value: T) -> Option<T> {
77///         self.inner.insert(entity, value)
78///     }
79///
80///     fn remove(&mut self, entity: Entity) -> Option<T> {
81///         self.inner.remove(entity)
82///     }
83///
84///     fn get(&self, entity: Entity) -> Option<&T> {
85///         self.inner.get(entity)
86///     }
87///
88///     fn get_mut(&mut self, entity: Entity) -> Option<&mut T> {
89///         self.inner.get_mut(entity)
90///     }
91///
92///     fn contains(&self, entity: Entity) -> bool {
93///         self.inner.contains(entity)
94///     }
95///
96///     fn len(&self) -> usize {
97///         self.inner.len()
98///     }
99///
100///     fn is_empty(&self) -> bool {
101///         self.inner.is_empty()
102///     }
103/// }
104/// ```
105pub trait ComponentStorage: Send + Sync {
106    /// The component type stored in this storage.
107    type Item: Component;
108
109    /// Inserts a component for the given entity.
110    ///
111    /// If the entity already has a component, the old value is returned.
112    /// Otherwise, `None` is returned.
113    ///
114    /// # Arguments
115    ///
116    /// * `entity` - The entity to associate with the component
117    /// * `value` - The component value to store
118    ///
119    /// # Returns
120    ///
121    /// The previous component value if one existed, or `None`.
122    ///
123    /// # Panics
124    ///
125    /// Implementations may panic if `entity` is invalid (e.g., placeholder).
126    fn insert(&mut self, entity: Entity, value: Self::Item) -> Option<Self::Item>;
127
128    /// Removes the component for the given entity.
129    ///
130    /// # Arguments
131    ///
132    /// * `entity` - The entity whose component to remove
133    ///
134    /// # Returns
135    ///
136    /// The removed component if one existed, or `None`.
137    fn remove(&mut self, entity: Entity) -> Option<Self::Item>;
138
139    /// Returns a reference to the component for the given entity.
140    ///
141    /// # Arguments
142    ///
143    /// * `entity` - The entity to look up
144    ///
145    /// # Returns
146    ///
147    /// A reference to the component if the entity has one, or `None`.
148    fn get(&self, entity: Entity) -> Option<&Self::Item>;
149
150    /// Returns a mutable reference to the component for the given entity.
151    ///
152    /// # Arguments
153    ///
154    /// * `entity` - The entity to look up
155    ///
156    /// # Returns
157    ///
158    /// A mutable reference to the component if the entity has one, or `None`.
159    fn get_mut(&mut self, entity: Entity) -> Option<&mut Self::Item>;
160
161    /// Returns `true` if the entity has a component in this storage.
162    ///
163    /// # Arguments
164    ///
165    /// * `entity` - The entity to check
166    fn contains(&self, entity: Entity) -> bool;
167
168    /// Returns the number of components in this storage.
169    fn len(&self) -> usize;
170
171    /// Returns `true` if this storage contains no components.
172    fn is_empty(&self) -> bool;
173}
174
175// =============================================================================
176// SparseSet Implementation
177// =============================================================================
178
179impl<T: Component> ComponentStorage for SparseSet<T> {
180    type Item = T;
181
182    #[inline]
183    fn insert(&mut self, entity: Entity, value: T) -> Option<T> {
184        SparseSet::insert(self, entity, value)
185    }
186
187    #[inline]
188    fn remove(&mut self, entity: Entity) -> Option<T> {
189        SparseSet::remove(self, entity)
190    }
191
192    #[inline]
193    fn get(&self, entity: Entity) -> Option<&T> {
194        SparseSet::get(self, entity)
195    }
196
197    #[inline]
198    fn get_mut(&mut self, entity: Entity) -> Option<&mut T> {
199        SparseSet::get_mut(self, entity)
200    }
201
202    #[inline]
203    fn contains(&self, entity: Entity) -> bool {
204        SparseSet::contains(self, entity)
205    }
206
207    #[inline]
208    fn len(&self) -> usize {
209        SparseSet::len(self)
210    }
211
212    #[inline]
213    fn is_empty(&self) -> bool {
214        SparseSet::is_empty(self)
215    }
216}
217
218// =============================================================================
219// Type-Erased Storage
220// =============================================================================
221
222/// Type-erased component storage for runtime operations.
223///
224/// Unlike [`ComponentStorage`], this trait is object-safe and can be used for
225/// type-erased storage in the ECS world. It provides basic operations that
226/// don't require knowing the concrete component type.
227///
228/// # Usage
229///
230/// This trait is primarily used internally by the ECS world to manage storage
231/// for different component types. Most users should interact with components
232/// through the typed [`ComponentStorage`] trait.
233///
234/// # Example
235///
236/// ```
237/// use goud_engine::ecs::{Entity, Component, SparseSet, AnyComponentStorage};
238///
239/// struct Position { x: f32, y: f32 }
240/// impl Component for Position {}
241///
242/// let mut storage: Box<dyn AnyComponentStorage> = Box::new(SparseSet::<Position>::new());
243///
244/// let entity = Entity::new(0, 1);
245///
246/// // Type-erased operations
247/// assert!(!storage.contains_entity(entity));
248/// assert_eq!(storage.storage_len(), 0);
249/// assert!(storage.storage_is_empty());
250/// ```
251pub trait AnyComponentStorage: Send + Sync {
252    /// Returns `true` if the entity has a component in this storage.
253    fn contains_entity(&self, entity: Entity) -> bool;
254
255    /// Removes the component for the given entity (dropping the value).
256    ///
257    /// Returns `true` if a component was removed, `false` otherwise.
258    fn remove_entity(&mut self, entity: Entity) -> bool;
259
260    /// Returns the number of components in this storage.
261    fn storage_len(&self) -> usize;
262
263    /// Returns `true` if this storage contains no components.
264    fn storage_is_empty(&self) -> bool;
265
266    /// Clears all components from this storage.
267    fn clear(&mut self);
268
269    /// Returns the type name of the stored component (for debugging).
270    fn component_type_name(&self) -> &'static str;
271}
272
273impl<T: Component> AnyComponentStorage for SparseSet<T> {
274    #[inline]
275    fn contains_entity(&self, entity: Entity) -> bool {
276        self.contains(entity)
277    }
278
279    #[inline]
280    fn remove_entity(&mut self, entity: Entity) -> bool {
281        self.remove(entity).is_some()
282    }
283
284    #[inline]
285    fn storage_len(&self) -> usize {
286        self.len()
287    }
288
289    #[inline]
290    fn storage_is_empty(&self) -> bool {
291        self.is_empty()
292    }
293
294    #[inline]
295    fn clear(&mut self) {
296        SparseSet::clear(self)
297    }
298
299    #[inline]
300    fn component_type_name(&self) -> &'static str {
301        std::any::type_name::<T>()
302    }
303}
304
305// =============================================================================
306// Unit Tests
307// =============================================================================
308
309#[cfg(test)]
310mod tests {
311    use super::*;
312
313    // Test components
314    #[derive(Debug, Clone, Copy, PartialEq)]
315    struct Position {
316        x: f32,
317        y: f32,
318    }
319    impl Component for Position {}
320
321    #[derive(Debug, Clone, Copy, PartialEq)]
322    struct Velocity {
323        x: f32,
324        y: f32,
325    }
326    impl Component for Velocity {}
327
328    #[derive(Debug, Clone, PartialEq)]
329    struct Name(String);
330    impl Component for Name {}
331
332    // Marker component (zero-sized)
333    #[derive(Debug, Clone, Copy, PartialEq)]
334    struct Player;
335    impl Component for Player {}
336
337    // =========================================================================
338    // ComponentStorage Trait Tests
339    // =========================================================================
340
341    mod component_storage_trait {
342        use super::*;
343
344        #[test]
345        fn test_sparse_set_implements_component_storage() {
346            // Verify SparseSet<T: Component> implements ComponentStorage
347            fn assert_component_storage<S: ComponentStorage>() {}
348            assert_component_storage::<SparseSet<Position>>();
349            assert_component_storage::<SparseSet<Velocity>>();
350            assert_component_storage::<SparseSet<Name>>();
351            assert_component_storage::<SparseSet<Player>>();
352        }
353
354        #[test]
355        fn test_component_storage_is_send_sync() {
356            fn assert_send_sync<T: Send + Sync>() {}
357            assert_send_sync::<SparseSet<Position>>();
358            assert_send_sync::<SparseSet<Name>>();
359        }
360
361        #[test]
362        fn test_insert_via_trait() {
363            let mut storage: SparseSet<Position> = SparseSet::new();
364            let entity = Entity::new(0, 1);
365
366            let old = ComponentStorage::insert(&mut storage, entity, Position { x: 1.0, y: 2.0 });
367
368            assert_eq!(old, None);
369            assert!(storage.contains(entity));
370        }
371
372        #[test]
373        fn test_insert_replace_via_trait() {
374            let mut storage: SparseSet<Position> = SparseSet::new();
375            let entity = Entity::new(0, 1);
376
377            ComponentStorage::insert(&mut storage, entity, Position { x: 1.0, y: 2.0 });
378            let old = ComponentStorage::insert(&mut storage, entity, Position { x: 3.0, y: 4.0 });
379
380            assert_eq!(old, Some(Position { x: 1.0, y: 2.0 }));
381            assert_eq!(
382                ComponentStorage::get(&storage, entity),
383                Some(&Position { x: 3.0, y: 4.0 })
384            );
385        }
386
387        #[test]
388        fn test_remove_via_trait() {
389            let mut storage: SparseSet<Position> = SparseSet::new();
390            let entity = Entity::new(0, 1);
391
392            storage.insert(entity, Position { x: 1.0, y: 2.0 });
393            let removed = ComponentStorage::remove(&mut storage, entity);
394
395            assert_eq!(removed, Some(Position { x: 1.0, y: 2.0 }));
396            assert!(!storage.contains(entity));
397        }
398
399        #[test]
400        fn test_remove_nonexistent_via_trait() {
401            let mut storage: SparseSet<Position> = SparseSet::new();
402            let entity = Entity::new(0, 1);
403
404            let removed = ComponentStorage::remove(&mut storage, entity);
405            assert_eq!(removed, None);
406        }
407
408        #[test]
409        fn test_get_via_trait() {
410            let mut storage: SparseSet<Position> = SparseSet::new();
411            let entity = Entity::new(0, 1);
412
413            storage.insert(entity, Position { x: 5.0, y: 10.0 });
414            let pos = ComponentStorage::get(&storage, entity);
415
416            assert_eq!(pos, Some(&Position { x: 5.0, y: 10.0 }));
417        }
418
419        #[test]
420        fn test_get_nonexistent_via_trait() {
421            let storage: SparseSet<Position> = SparseSet::new();
422            let entity = Entity::new(0, 1);
423
424            assert_eq!(ComponentStorage::get(&storage, entity), None);
425        }
426
427        #[test]
428        fn test_get_mut_via_trait() {
429            let mut storage: SparseSet<Position> = SparseSet::new();
430            let entity = Entity::new(0, 1);
431
432            storage.insert(entity, Position { x: 1.0, y: 2.0 });
433
434            if let Some(pos) = ComponentStorage::get_mut(&mut storage, entity) {
435                pos.x = 100.0;
436                pos.y = 200.0;
437            }
438
439            assert_eq!(storage.get(entity), Some(&Position { x: 100.0, y: 200.0 }));
440        }
441
442        #[test]
443        fn test_contains_via_trait() {
444            let mut storage: SparseSet<Position> = SparseSet::new();
445            let e1 = Entity::new(0, 1);
446            let e2 = Entity::new(1, 1);
447
448            storage.insert(e1, Position { x: 0.0, y: 0.0 });
449
450            assert!(ComponentStorage::contains(&storage, e1));
451            assert!(!ComponentStorage::contains(&storage, e2));
452        }
453
454        #[test]
455        fn test_len_via_trait() {
456            let mut storage: SparseSet<Position> = SparseSet::new();
457
458            assert_eq!(ComponentStorage::len(&storage), 0);
459
460            storage.insert(Entity::new(0, 1), Position { x: 0.0, y: 0.0 });
461            assert_eq!(ComponentStorage::len(&storage), 1);
462
463            storage.insert(Entity::new(1, 1), Position { x: 1.0, y: 1.0 });
464            assert_eq!(ComponentStorage::len(&storage), 2);
465        }
466
467        #[test]
468        fn test_is_empty_via_trait() {
469            let mut storage: SparseSet<Position> = SparseSet::new();
470
471            assert!(ComponentStorage::is_empty(&storage));
472
473            storage.insert(Entity::new(0, 1), Position { x: 0.0, y: 0.0 });
474            assert!(!ComponentStorage::is_empty(&storage));
475        }
476
477        #[test]
478        fn test_generic_function_with_component_storage() {
479            // Test that we can write generic code over ComponentStorage
480            #[allow(dead_code)]
481            fn count_matching<S>(storage: &S, predicate: impl Fn(&Position) -> bool) -> usize
482            where
483                S: ComponentStorage<Item = Position> + AsRef<SparseSet<Position>>,
484            {
485                storage
486                    .as_ref()
487                    .iter()
488                    .filter(|(_, pos)| predicate(pos))
489                    .count()
490            }
491
492            // Note: This requires a custom wrapper or direct iteration
493            // For now, just verify the trait works with the methods
494            let mut storage: SparseSet<Position> = SparseSet::new();
495            storage.insert(Entity::new(0, 1), Position { x: 1.0, y: 2.0 });
496            storage.insert(Entity::new(1, 1), Position { x: 3.0, y: 4.0 });
497
498            assert_eq!(ComponentStorage::len(&storage), 2);
499        }
500    }
501
502    // =========================================================================
503    // AnyComponentStorage Tests
504    // =========================================================================
505
506    mod any_component_storage {
507        use super::*;
508
509        #[test]
510        fn test_sparse_set_implements_any_component_storage() {
511            fn assert_any_storage<S: AnyComponentStorage>() {}
512            assert_any_storage::<SparseSet<Position>>();
513            assert_any_storage::<SparseSet<Name>>();
514        }
515
516        #[test]
517        fn test_any_storage_is_object_safe() {
518            // Verify we can create trait objects
519            let _storage: Box<dyn AnyComponentStorage> = Box::new(SparseSet::<Position>::new());
520        }
521
522        #[test]
523        fn test_contains_entity_via_any() {
524            let storage: Box<dyn AnyComponentStorage> = Box::new(SparseSet::<Position>::new());
525
526            let entity = Entity::new(0, 1);
527
528            assert!(!storage.contains_entity(entity));
529
530            // We need to downcast to insert - this is expected
531            // Type-erased storage is for querying, not typed operations
532        }
533
534        #[test]
535        fn test_storage_len_via_any() {
536            let mut storage = SparseSet::<Position>::new();
537            storage.insert(Entity::new(0, 1), Position { x: 0.0, y: 0.0 });
538            storage.insert(Entity::new(1, 1), Position { x: 1.0, y: 1.0 });
539
540            let any_storage: &dyn AnyComponentStorage = &storage;
541
542            assert_eq!(any_storage.storage_len(), 2);
543            assert!(!any_storage.storage_is_empty());
544        }
545
546        #[test]
547        fn test_remove_entity_via_any() {
548            let mut storage = SparseSet::<Position>::new();
549            let entity = Entity::new(0, 1);
550
551            storage.insert(entity, Position { x: 1.0, y: 2.0 });
552
553            let any_storage: &mut dyn AnyComponentStorage = &mut storage;
554
555            assert!(any_storage.contains_entity(entity));
556            let removed = any_storage.remove_entity(entity);
557
558            assert!(removed);
559            assert!(!any_storage.contains_entity(entity));
560        }
561
562        #[test]
563        fn test_remove_nonexistent_via_any() {
564            let mut storage = SparseSet::<Position>::new();
565            let entity = Entity::new(0, 1);
566
567            let any_storage: &mut dyn AnyComponentStorage = &mut storage;
568            let removed = any_storage.remove_entity(entity);
569
570            assert!(!removed);
571        }
572
573        #[test]
574        fn test_clear_via_any() {
575            let mut storage = SparseSet::<Position>::new();
576            storage.insert(Entity::new(0, 1), Position { x: 0.0, y: 0.0 });
577            storage.insert(Entity::new(1, 1), Position { x: 1.0, y: 1.0 });
578
579            let any_storage: &mut dyn AnyComponentStorage = &mut storage;
580            any_storage.clear();
581
582            assert!(any_storage.storage_is_empty());
583            assert_eq!(any_storage.storage_len(), 0);
584        }
585
586        #[test]
587        fn test_component_type_name_via_any() {
588            let storage = SparseSet::<Position>::new();
589            let any_storage: &dyn AnyComponentStorage = &storage;
590
591            let name = any_storage.component_type_name();
592            assert!(name.contains("Position"));
593        }
594
595        #[test]
596        fn test_multiple_storages_as_any() {
597            let mut positions = SparseSet::<Position>::new();
598            let mut velocities = SparseSet::<Velocity>::new();
599
600            positions.insert(Entity::new(0, 1), Position { x: 1.0, y: 2.0 });
601            velocities.insert(Entity::new(0, 1), Velocity { x: 3.0, y: 4.0 });
602
603            let storages: Vec<&dyn AnyComponentStorage> = vec![&positions, &velocities];
604
605            assert_eq!(storages[0].storage_len(), 1);
606            assert_eq!(storages[1].storage_len(), 1);
607
608            assert!(storages[0].component_type_name().contains("Position"));
609            assert!(storages[1].component_type_name().contains("Velocity"));
610        }
611
612        #[test]
613        fn test_any_storage_is_empty() {
614            let storage: Box<dyn AnyComponentStorage> = Box::new(SparseSet::<Position>::new());
615            assert!(storage.storage_is_empty());
616        }
617    }
618
619    // =========================================================================
620    // Integration Tests
621    // =========================================================================
622
623    mod integration {
624        use super::*;
625
626        #[test]
627        fn test_component_storage_workflow() {
628            let mut storage: SparseSet<Position> = SparseSet::new();
629
630            // Create entities
631            let e1 = Entity::new(0, 1);
632            let e2 = Entity::new(1, 1);
633            let e3 = Entity::new(2, 1);
634
635            // Insert via trait
636            ComponentStorage::insert(&mut storage, e1, Position { x: 0.0, y: 0.0 });
637            ComponentStorage::insert(&mut storage, e2, Position { x: 10.0, y: 20.0 });
638            ComponentStorage::insert(&mut storage, e3, Position { x: 100.0, y: 200.0 });
639
640            assert_eq!(ComponentStorage::len(&storage), 3);
641
642            // Modify via trait
643            if let Some(pos) = ComponentStorage::get_mut(&mut storage, e2) {
644                pos.x += 5.0;
645                pos.y += 5.0;
646            }
647
648            // Verify
649            assert_eq!(
650                ComponentStorage::get(&storage, e2),
651                Some(&Position { x: 15.0, y: 25.0 })
652            );
653
654            // Remove via trait
655            let removed = ComponentStorage::remove(&mut storage, e1);
656            assert_eq!(removed, Some(Position { x: 0.0, y: 0.0 }));
657            assert_eq!(ComponentStorage::len(&storage), 2);
658
659            // Remaining entities still accessible
660            assert!(ComponentStorage::contains(&storage, e2));
661            assert!(ComponentStorage::contains(&storage, e3));
662        }
663
664        #[test]
665        fn test_zero_sized_component_storage() {
666            let mut storage: SparseSet<Player> = SparseSet::new();
667
668            let e1 = Entity::new(0, 1);
669            let e2 = Entity::new(1, 1);
670
671            ComponentStorage::insert(&mut storage, e1, Player);
672            ComponentStorage::insert(&mut storage, e2, Player);
673
674            assert_eq!(ComponentStorage::len(&storage), 2);
675            assert!(ComponentStorage::contains(&storage, e1));
676            assert!(ComponentStorage::contains(&storage, e2));
677
678            // ZST storage works correctly
679            assert_eq!(ComponentStorage::get(&storage, e1), Some(&Player));
680        }
681
682        #[test]
683        fn test_string_component_storage() {
684            let mut storage: SparseSet<Name> = SparseSet::new();
685
686            let entity = Entity::new(0, 1);
687            ComponentStorage::insert(&mut storage, entity, Name("Player".to_string()));
688
689            assert_eq!(
690                ComponentStorage::get(&storage, entity),
691                Some(&Name("Player".to_string()))
692            );
693
694            // Replace
695            let old = ComponentStorage::insert(&mut storage, entity, Name("Enemy".to_string()));
696            assert_eq!(old, Some(Name("Player".to_string())));
697        }
698    }
699}