goud_engine/ecs/storage/traits.rs
1//! Component storage trait definitions.
2//!
3//! This module defines the [`ComponentStorage`] trait, which provides an abstract
4//! interface for component storage backends, and [`AnyComponentStorage`], which
5//! is the object-safe type-erased variant.
6
7use crate::ecs::{Component, Entity};
8
9/// Trait for component storage backends.
10///
11/// `ComponentStorage` defines the interface for storing components indexed by entity.
12/// Any type implementing this trait can be used as a storage backend for components
13/// in the ECS world.
14///
15/// # Type Parameters
16///
17/// The associated type `Item` specifies what component type this storage holds.
18/// It must implement [`Component`] to ensure thread safety requirements are met.
19///
20/// # Thread Safety
21///
22/// Implementations must be `Send + Sync` to allow the ECS world to be shared
23/// across threads. This is enforced by the trait bounds.
24///
25/// # Object Safety
26///
27/// This trait is **not** object-safe due to the associated type. For type-erased
28/// storage, use [`AnyComponentStorage`] which provides a runtime interface.
29///
30/// # Example Implementation
31///
32/// ```
33/// use goud_engine::ecs::{Entity, Component, ComponentStorage, SparseSet};
34///
35/// // Define a custom storage (wrapping SparseSet for this example)
36/// struct MyStorage<T: Component> {
37/// inner: SparseSet<T>,
38/// }
39///
40/// impl<T: Component> ComponentStorage for MyStorage<T> {
41/// type Item = T;
42///
43/// fn insert(&mut self, entity: Entity, value: T) -> Option<T> {
44/// self.inner.insert(entity, value)
45/// }
46///
47/// fn remove(&mut self, entity: Entity) -> Option<T> {
48/// self.inner.remove(entity)
49/// }
50///
51/// fn get(&self, entity: Entity) -> Option<&T> {
52/// self.inner.get(entity)
53/// }
54///
55/// fn get_mut(&mut self, entity: Entity) -> Option<&mut T> {
56/// self.inner.get_mut(entity)
57/// }
58///
59/// fn contains(&self, entity: Entity) -> bool {
60/// self.inner.contains(entity)
61/// }
62///
63/// fn len(&self) -> usize {
64/// self.inner.len()
65/// }
66///
67/// fn is_empty(&self) -> bool {
68/// self.inner.is_empty()
69/// }
70/// }
71/// ```
72pub trait ComponentStorage: Send + Sync {
73 /// The component type stored in this storage.
74 type Item: Component;
75
76 /// Inserts a component for the given entity.
77 ///
78 /// If the entity already has a component, the old value is returned.
79 /// Otherwise, `None` is returned.
80 ///
81 /// # Arguments
82 ///
83 /// * `entity` - The entity to associate with the component
84 /// * `value` - The component value to store
85 ///
86 /// # Returns
87 ///
88 /// The previous component value if one existed, or `None`.
89 ///
90 /// # Panics
91 ///
92 /// Implementations may panic if `entity` is invalid (e.g., placeholder).
93 fn insert(&mut self, entity: Entity, value: Self::Item) -> Option<Self::Item>;
94
95 /// Removes the component for the given entity.
96 ///
97 /// # Arguments
98 ///
99 /// * `entity` - The entity whose component to remove
100 ///
101 /// # Returns
102 ///
103 /// The removed component if one existed, or `None`.
104 fn remove(&mut self, entity: Entity) -> Option<Self::Item>;
105
106 /// Returns a reference to the component for the given entity.
107 ///
108 /// # Arguments
109 ///
110 /// * `entity` - The entity to look up
111 ///
112 /// # Returns
113 ///
114 /// A reference to the component if the entity has one, or `None`.
115 fn get(&self, entity: Entity) -> Option<&Self::Item>;
116
117 /// Returns a mutable reference to the component for the given entity.
118 ///
119 /// # Arguments
120 ///
121 /// * `entity` - The entity to look up
122 ///
123 /// # Returns
124 ///
125 /// A mutable reference to the component if the entity has one, or `None`.
126 fn get_mut(&mut self, entity: Entity) -> Option<&mut Self::Item>;
127
128 /// Returns `true` if the entity has a component in this storage.
129 ///
130 /// # Arguments
131 ///
132 /// * `entity` - The entity to check
133 fn contains(&self, entity: Entity) -> bool;
134
135 /// Returns the number of components in this storage.
136 fn len(&self) -> usize;
137
138 /// Returns `true` if this storage contains no components.
139 fn is_empty(&self) -> bool;
140}
141
142/// Type-erased component storage for runtime operations.
143///
144/// Unlike [`ComponentStorage`], this trait is object-safe and can be used for
145/// type-erased storage in the ECS world. It provides basic operations that
146/// don't require knowing the concrete component type.
147///
148/// # Usage
149///
150/// This trait is primarily used internally by the ECS world to manage storage
151/// for different component types. Most users should interact with components
152/// through the typed [`ComponentStorage`] trait.
153///
154/// # Example
155///
156/// ```
157/// use goud_engine::ecs::{Entity, Component, SparseSet, AnyComponentStorage};
158///
159/// struct Position { x: f32, y: f32 }
160/// impl Component for Position {}
161///
162/// let mut storage: Box<dyn AnyComponentStorage> = Box::new(SparseSet::<Position>::new());
163///
164/// let entity = Entity::new(0, 1);
165///
166/// // Type-erased operations
167/// assert!(!storage.contains_entity(entity));
168/// assert_eq!(storage.storage_len(), 0);
169/// assert!(storage.storage_is_empty());
170/// ```
171pub trait AnyComponentStorage: Send + Sync {
172 /// Returns `true` if the entity has a component in this storage.
173 fn contains_entity(&self, entity: Entity) -> bool;
174
175 /// Removes the component for the given entity (dropping the value).
176 ///
177 /// Returns `true` if a component was removed, `false` otherwise.
178 fn remove_entity(&mut self, entity: Entity) -> bool;
179
180 /// Returns the number of components in this storage.
181 fn storage_len(&self) -> usize;
182
183 /// Returns `true` if this storage contains no components.
184 fn storage_is_empty(&self) -> bool;
185
186 /// Clears all components from this storage.
187 fn clear(&mut self);
188
189 /// Returns the type name of the stored component (for debugging).
190 fn component_type_name(&self) -> &'static str;
191}