Skip to main content

ixa/entity/
entity.rs

1/*!
2
3An implementor of [`Entity`] is a type that names a collection of related properties in
4analogy to a table in a database. The properties are analogous to the columns in the table,
5and the [`EntityId<E>`] type is analogous to the primary key of the table, the row number.
6
7[`Entity`]s are declared with the [`define_entity!`] macro:
8
9```rust
10use ixa::define_entity;
11define_entity!(Person);
12```
13
14Once an [`Entity`] is defined, [`Property`]s can be defined for the [`Entity`]. See the
15[`property`](crate::entity::property) module.
16
17Right now an `Entity` type is just a zero-sized marker type. The static data associated with the type isn't used yet.
18
19*/
20
21use std::any::{Any, TypeId};
22use std::fmt::{Debug, Display, Formatter};
23use std::hash::{Hash, Hasher};
24use std::marker::PhantomData;
25
26use serde::{Deserialize, Serialize};
27
28use super::entity_store::get_entity_metadata_static;
29
30/// A type that can be named and used (copied, cloned) but not created outside of this crate.
31/// In the `define_entity!` macro we define the alias `pub type MyEntityId = EntityId<MyEntity>`.
32#[derive(Serialize, Deserialize)]
33#[serde(transparent)]
34pub struct EntityId<E: Entity>(pub(crate) usize, PhantomData<E>);
35// Note: The generics on `EntityId<E>` prevent the compiler from "seeing" the derived traits in some client code,
36//       so we provide blanket implementations below.
37
38// Otherwise the compiler isn't smart enough to know `EntityId<E>` is always `PartialEq`/`Eq`
39impl<E: Entity> PartialEq for EntityId<E> {
40    fn eq(&self, other: &Self) -> bool {
41        self.0 == other.0
42    }
43}
44impl<E: Entity> Eq for EntityId<E> {}
45
46// Otherwise the compiler isn't smart enough to know `EntityId<E>` is always `Clone`
47impl<E: Entity> Clone for EntityId<E> {
48    #[inline]
49    fn clone(&self) -> Self {
50        *self
51    }
52}
53
54// Otherwise the compiler isn't smart enough to know `EntityId<E>` is always `Copy`
55impl<E: Entity> Copy for EntityId<E> {}
56
57// The value `EntityId<Person>(7, PhantomData<Person>)` has `Debug` display "PersonId(7)".
58impl<E: Entity> Debug for EntityId<E> {
59    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
60        let name = format!("{}Id", E::name());
61        f.debug_tuple(name.as_str()).field(&self.0).finish()
62    }
63}
64// The value `EntityId<Person>(7, PhantomData<Person>)` will display as "7".
65impl<E: Entity> Display for EntityId<E> {
66    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
67        write!(f, "{}", self.0)
68    }
69}
70
71// Otherwise the compiler isn't smart enough to know `EntityId<E>` is always `Hash`
72impl<E: Entity> Hash for EntityId<E> {
73    fn hash<H: Hasher>(&self, state: &mut H) {
74        self.0.hash(state);
75    }
76}
77
78impl<E: Entity> EntityId<E> {
79    /// Only constructible from this crate.
80    pub(crate) fn new(index: usize) -> Self {
81        Self(index, PhantomData)
82    }
83}
84
85/// All entities must implement this trait using the `define_entity!` macro.
86pub trait Entity: Any + Default {
87    #[must_use]
88    fn name() -> &'static str {
89        let full = std::any::type_name::<Self>();
90        // Splitting any string, including an empty one, always yields at least one segment.
91        full.rsplit("::").next().unwrap()
92    }
93
94    #[must_use]
95    fn type_id() -> TypeId {
96        TypeId::of::<Self>()
97    }
98
99    /// Get a list of all properties this `Entity` has. This list is static, computed in with `ctor` magic.
100    #[must_use]
101    fn property_ids() -> &'static [TypeId] {
102        let (property_ids, _) = get_entity_metadata_static(<Self as Entity>::type_id());
103        property_ids
104    }
105
106    /// Get a list of all properties of this `Entity` that _must_ be supplied when a new entity is created.
107    #[must_use]
108    fn required_property_ids() -> &'static [TypeId] {
109        let (_, required_property_ids) = get_entity_metadata_static(<Self as Entity>::type_id());
110        required_property_ids
111    }
112
113    /// The index of this item in the owner, which is initialized globally per type
114    /// upon first access. We explicitly initialize this in a `ctor` in order to know
115    /// how many [`Entity`] types exist globally when we construct any `EntityStore`.
116    #[must_use]
117    fn id() -> usize;
118
119    /// Returns the process-local profiling identity for this entity's whole-population query.
120    ///
121    /// This is macro support and is not a stable identifier. Entity implementations generated
122    /// by Ixa macros override it with a per-concrete-type cache.
123    #[doc(hidden)]
124    fn all_query_identity() -> usize {
125        panic!("Entity implementations must be created with define_entity! or impl_entity!")
126    }
127
128    /// Creates a new boxed instance of the item.
129    #[must_use]
130    fn new_boxed() -> Box<Self> {
131        Box::default()
132    }
133
134    /// Standard pattern for downcasting to concrete types.
135    fn as_any(&self) -> &dyn Any;
136    fn as_any_mut(&mut self) -> &mut dyn Any;
137}
138
139pub type BxEntity = Box<dyn Entity>;
140
141/// An iterator over the total population of `EntityId<E>`s at the time of iterator creation.
142///
143/// If entities are added _after_ this iterator has been created, this iterator will _not_ produce the `EntityId<E>`s
144/// of the newly added entities.
145#[derive(Copy, Clone)]
146pub struct PopulationIterator<E: Entity> {
147    /// The total count of all entities of this type at the time this iterator was created
148    population: usize,
149    /// The next `EntityId<E>` this iterator will produce (assuming `entity_id < population`)
150    entity_id: usize,
151
152    _phantom: PhantomData<E>,
153}
154
155impl<E: Entity> PopulationIterator<E> {
156    // Only internal Ixa code can create a new `PopulationIterator<E>` in order to guarantee only valid
157    // `EntityId<E>` values are ever created.
158    pub(crate) fn new(population: usize) -> Self {
159        PopulationIterator::<E> {
160            population,
161            entity_id: 0,
162            _phantom: PhantomData,
163        }
164    }
165
166    #[must_use]
167    pub(crate) fn population(&self) -> usize {
168        self.population
169    }
170}
171
172impl<E: Entity> Iterator for PopulationIterator<E> {
173    type Item = EntityId<E>;
174
175    fn next(&mut self) -> Option<Self::Item> {
176        if self.entity_id < self.population {
177            let current_id = self.entity_id;
178            // `self.entity_id` saturates to `self.population`.
179            self.entity_id += 1;
180            Some(EntityId::new(current_id))
181        } else {
182            None
183        }
184    }
185
186    // This iterator knows how many elements it will iterate over.
187    fn size_hint(&self) -> (usize, Option<usize>) {
188        let remaining = self.len();
189        (remaining, Some(remaining))
190    }
191
192    // Fast consuming count
193    fn count(self) -> usize {
194        self.len()
195    }
196
197    // Fast "seeking" operation.
198    fn nth(&mut self, n: usize) -> Option<Self::Item> {
199        // `self.entity_id` saturates to `self.population`.
200        self.entity_id = (self.entity_id + n).min(self.population);
201        self.next()
202    }
203}
204
205impl<E: Entity> ExactSizeIterator for PopulationIterator<E> {
206    fn len(&self) -> usize {
207        // Safety: Since `self.entity_id` saturates to `self.population`, we do not need `saturating_sub` here.
208        self.population - self.entity_id
209    }
210}
211// Once `PopulationIterator<E>` returns `None`, it will always return `None`.
212impl<E: Entity> std::iter::FusedIterator for PopulationIterator<E> {}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217    use crate::define_entity;
218
219    define_entity!(DummyEntity);
220
221    #[test]
222    fn entity_id_debug_display() {
223        let entity_id = DummyEntityId::new(137);
224        assert_eq!(format!("{:?}", entity_id), "DummyEntityId(137)");
225        assert_eq!(format!("{}", entity_id), "137");
226    }
227
228    #[test]
229    fn define_entity_helpers_create_and_downcast() {
230        assert_eq!(DummyEntity::new(), DummyEntity);
231
232        let mut entity = DummyEntity::new();
233        assert!(entity.as_any().downcast_ref::<DummyEntity>().is_some());
234        assert!(entity.as_any_mut().downcast_mut::<DummyEntity>().is_some());
235    }
236
237    #[test]
238    fn test_entity_iterator_basic() {
239        let mut iter = PopulationIterator::<DummyEntity>::new(3);
240
241        assert_eq!(iter.len(), 3);
242        assert_eq!(iter.next(), Some(EntityId::new(0)));
243        assert_eq!(iter.len(), 2);
244        assert_eq!(iter.next(), Some(EntityId::new(1)));
245        assert_eq!(iter.len(), 1);
246        assert_eq!(iter.next(), Some(EntityId::new(2)));
247        assert_eq!(iter.len(), 0);
248        assert_eq!(iter.next(), None);
249        assert_eq!(iter.len(), 0);
250        assert_eq!(iter.next(), None); // FusedIterator behavior
251    }
252
253    #[test]
254    fn test_entity_iterator_nth() {
255        let mut iter = PopulationIterator::<DummyEntity>::new(10);
256
257        // Seek to 3rd element (index 2)
258        assert_eq!(iter.nth(2), Some(EntityId::new(2)));
259        assert_eq!(iter.len(), 7);
260
261        // Seek relative to current position (+1 means skip index 3, return 4)
262        assert_eq!(iter.nth(1), Some(EntityId::new(4)));
263
264        // Seek past end
265        assert_eq!(iter.nth(10), None);
266        assert_eq!(iter.len(), 0);
267        assert_eq!(iter.next(), None);
268    }
269
270    #[test]
271    fn test_entity_iterator_size_hint() {
272        let mut iter = PopulationIterator::<DummyEntity>::new(5);
273        assert_eq!(iter.size_hint(), (5, Some(5)));
274
275        iter.next();
276        assert_eq!(iter.size_hint(), (4, Some(4)));
277
278        // Seek past end
279        assert_eq!(iter.nth(10), None);
280        assert_eq!(iter.size_hint(), (0, Some(0)));
281    }
282
283    #[test]
284    fn test_entity_iterator_clonable() {
285        let mut iter = PopulationIterator::<DummyEntity>::new(5);
286        iter.next();
287
288        let mut cloned = iter;
289        assert_eq!(iter.next(), cloned.next());
290        assert_eq!(iter.size_hint(), cloned.size_hint());
291    }
292}