diff --git a/framework_crates/bones_ecs/src/components/iterator.rs b/framework_crates/bones_ecs/src/components/iterator.rs index 50babfc8c6..c74ef38460 100644 --- a/framework_crates/bones_ecs/src/components/iterator.rs +++ b/framework_crates/bones_ecs/src/components/iterator.rs @@ -38,13 +38,13 @@ impl<'a> Iterator for UntypedComponentBitsetIterator<'a> { type Item = SchemaRef<'a>; fn next(&mut self) -> Option { let max_id = self.components.max_id; - while !(self.bitset.bit_test(self.current_id) - && self.components.bitset.bit_test(self.current_id)) - && self.current_id <= max_id + while self.current_id < max_id + && !(self.bitset.bit_test(self.current_id) + && self.components.bitset.bit_test(self.current_id)) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: Here we are just getting a pointer, not doing anything unsafe with it. Some(unsafe { SchemaRef::from_ptr_schema( @@ -71,11 +71,11 @@ impl<'a> Iterator for UntypedComponentOptionalBitsetIterator<'a> { fn next(&mut self) -> Option { // We stop iterating at bitset length, not component store length, as we want to iterate over // whole bitset and return None for entities that don't have this optional component. - let max_id = self.bitset.bit_len() - 1; - while !self.bitset.bit_test(self.current_id) && self.current_id <= max_id { + let max_id = self.bitset.bit_len(); + while self.current_id < max_id && !self.bitset.bit_test(self.current_id) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: Here we are just getting a pointer, not doing anything unsafe with it. if self.components.bitset.bit_test(self.current_id) { Some(Some(unsafe { @@ -110,13 +110,13 @@ impl<'a> Iterator for UntypedComponentBitsetIteratorMut<'a> { type Item = SchemaRefMut<'a>; fn next(&mut self) -> Option { let max_id = self.components.max_id; - while !(self.bitset.bit_test(self.current_id) - && self.components.bitset.bit_test(self.current_id)) - && self.current_id <= max_id + while self.current_id < max_id + && !(self.bitset.bit_test(self.current_id) + && self.components.bitset.bit_test(self.current_id)) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: We know that the index is within bounds, and we know that the pointer will be // valid for the new lifetime. Some(unsafe { @@ -144,11 +144,11 @@ impl<'a> Iterator for UntypedComponentOptionalBitsetIteratorMut<'a> { fn next(&mut self) -> Option { // We do not stop iterating at component store length, as we want to iterate over // whole bitset and return None for entities that don't have this optional component. - let max_id = self.bitset.bit_len() - 1; - while !self.bitset.bit_test(self.current_id) && self.current_id <= max_id { + let max_id = self.bitset.bit_len(); + while self.current_id < max_id && !self.bitset.bit_test(self.current_id) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: Here we are just getting a pointer, not doing anything unsafe with it. if self.components.bitset.bit_test(self.current_id) { Some(Some(unsafe { diff --git a/framework_crates/bones_ecs/src/components/typed.rs b/framework_crates/bones_ecs/src/components/typed.rs index 23578ac186..7decc444cd 100644 --- a/framework_crates/bones_ecs/src/components/typed.rs +++ b/framework_crates/bones_ecs/src/components/typed.rs @@ -83,7 +83,7 @@ impl ComponentStore { self.untyped.get_mut_or_insert(entity, f) } - /// Get mutable references s to the component data for multiple entities at the same time. + /// Get mutable references to the component data for multiple entities at the same time. /// /// # Panics /// @@ -110,6 +110,27 @@ impl ComponentStore { self.untyped.remove(entity) } + /// Gets an immutable reference to the component if there is exactly one instance of it. + #[inline] + pub fn get_single_with_bitset(&self, bitset: Rc) -> Result<&T, QuerySingleError> { + // SOUND: we know the schema matches. + self.untyped + .get_single_with_bitset(bitset) + .map(|x| unsafe { x.cast_into_unchecked() }) + } + + /// Gets a mutable reference to the component if there is exactly one instance of it. + #[inline] + pub fn get_single_with_bitset_mut( + &mut self, + bitset: Rc, + ) -> Result<&mut T, QuerySingleError> { + // SOUND: we know the schema matches. + self.untyped + .get_single_with_bitset_mut(bitset) + .map(|x| unsafe { x.cast_into_mut_unchecked() }) + } + /// Iterates immutably over all components of this type. /// Very fast but doesn't allow joining with other component types. #[inline] @@ -137,6 +158,15 @@ impl ComponentStore { /// /// Automatically implemented for [`ComponentStore`]. pub trait ComponentIterBitset<'a, T: HasSchema> { + /// Gets an immutable reference to the component if there is exactly one instance of it. + fn get_single_with_bitset(&self, bitset: Rc) -> Result<&T, QuerySingleError>; + + /// Gets a mutable reference to the component if there is exactly one instance of it. + fn get_single_mut_with_bitset( + &mut self, + bitset: Rc, + ) -> Result<&mut T, QuerySingleError>; + /// Iterates immutably over the components of this type where `bitset` /// indicates the indices of entities. /// Slower than `iter()` but allows joining between multiple component types. @@ -174,6 +204,27 @@ pub trait ComponentIterBitset<'a, T: HasSchema> { } impl<'a, T: HasSchema> ComponentIterBitset<'a, T> for ComponentStore { + /// Gets an immutable reference to the component if there is exactly one instance of it. + fn get_single_with_bitset(&self, bitset: Rc) -> Result<&T, QuerySingleError> { + // SOUND: we know the schema matches. + fn map(r: SchemaRef) -> &T { + unsafe { r.cast_into_unchecked() } + } + self.untyped.get_single_with_bitset(bitset).map(map) + } + + /// Gets a mutable reference to the component if there is exactly one instance of it. + fn get_single_mut_with_bitset( + &mut self, + bitset: Rc, + ) -> Result<&mut T, QuerySingleError> { + // SOUND: we know the schema matches. + fn map(r: SchemaRefMut) -> &mut T { + unsafe { r.cast_into_mut_unchecked() } + } + self.untyped.get_single_with_bitset_mut(bitset).map(map) + } + /// Iterates immutably over the components of this type where `bitset` /// indicates the indices of entities. /// Slower than `iter()` but allows joining between multiple component types. @@ -207,7 +258,7 @@ impl<'a, T: HasSchema> ComponentIterBitset<'a, T> for ComponentStore { #[inline] fn iter_mut_with_bitset(&mut self, bitset: Rc) -> ComponentBitsetIteratorMut { // SOUND: we know the schema matches. - fn map(r: SchemaRefMut<'_>) -> &mut T { + fn map(r: SchemaRefMut) -> &mut T { unsafe { r.cast_into_mut_unchecked() } } @@ -250,14 +301,16 @@ impl<'a, T: HasSchema> ComponentIterBitset<'a, T> for ComponentStore { #[cfg(test)] mod tests { + use std::rc::Rc; + use crate::prelude::*; + #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] + #[repr(C)] + struct A(String); + #[test] fn create_remove_components() { - #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] - #[repr(C)] - struct A(String); - let mut entities = Entities::default(); let e1 = entities.create(); let e2 = entities.create(); @@ -276,10 +329,6 @@ mod tests { #[test] fn get_mut_or_insert() { - #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] - #[repr(C)] - struct A(String); - let mut entities = Entities::default(); let e1 = entities.create(); @@ -299,4 +348,129 @@ mod tests { // Test that existing component is retrieved assert_eq!(comp.0, "Test2"); } + + #[test] + fn single_returns_none_when_empty() { + let storage = ComponentStore::::default(); + let bitset = Rc::new({ + let mut entities = Entities::default(); + entities.create(); + entities.bitset().clone() + }); + + let maybe_comp = storage.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Err(QuerySingleError::NoEntities)); + } + + #[test] + fn single_returns_some_single() { + let mut storage = ComponentStore::::default(); + let mut entities = Entities::default(); + + // Create some dummies so that the target entity isn't 0 + (0..3).map(|_| entities.create()).count(); + + let e = entities.create(); + let a = A("a".to_string()); + storage.insert(e, a.clone()); + + let bitset = Rc::new(entities.bitset().clone()); + + let maybe_comp = storage.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Ok(&a)); + } + + #[test] + fn single_returns_none_when_more_than_1() { + let mut entities = Entities::default(); + let mut storage = ComponentStore::::default(); + + (0..3) + .map(|i| storage.insert(entities.create(), A(i.to_string()))) + .count(); + + let bitset = Rc::new(entities.bitset().clone()); + + let maybe_comp = storage.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Err(QuerySingleError::MultipleEntities)); + } + + #[test] + fn iter_with_bitset() { + let mut entities = Entities::default(); + let mut storage = ComponentStore::::default(); + + { + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset(bitset.clone()); + assert_eq!(comp_iter.next(), None); + + let mut comp_mut_iter = storage.iter_mut_with_bitset(bitset); + assert_eq!(comp_mut_iter.next(), None); + } + + { + let e = entities.create(); + let mut a = A("e".to_string()); + storage.insert(e, a.clone()); + + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset(bitset.clone()); + assert_eq!(comp_iter.next(), Some(&a)); + + let mut comp_mut_iter = storage.iter_mut_with_bitset(bitset); + assert_eq!(comp_mut_iter.next(), Some(&mut a)); + + entities.kill(e); + } + } + + #[test] + fn iter_with_bitset_optional() { + let mut entities = Entities::default(); + let mut storage = ComponentStore::::default(); + + { + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset_optional(bitset.clone()); + assert_eq!(comp_iter.next(), None); + + let mut comp_mut_iter = storage.iter_mut_with_bitset_optional(bitset); + assert_eq!(comp_mut_iter.next(), None); + } + + { + let e = entities.create(); + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset_optional(bitset.clone()); + assert_eq!(comp_iter.next(), Some(None)); + + let mut comp_mut_iter = storage.iter_mut_with_bitset_optional(bitset); + assert_eq!(comp_mut_iter.next(), Some(None)); + + entities.kill(e); + } + + { + let e = entities.create(); + let mut a = A("e".to_string()); + storage.insert(e, a.clone()); + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset_optional(bitset.clone()); + assert_eq!(comp_iter.next(), Some(Some(&a))); + + let mut comp_mut_iter = storage.iter_mut_with_bitset_optional(bitset); + assert_eq!(comp_mut_iter.next(), Some(Some(&mut a))); + + entities.kill(e); + } + } } diff --git a/framework_crates/bones_ecs/src/components/untyped.rs b/framework_crates/bones_ecs/src/components/untyped.rs index e5bf23e246..70697f6f64 100644 --- a/framework_crates/bones_ecs/src/components/untyped.rs +++ b/framework_crates/bones_ecs/src/components/untyped.rs @@ -295,12 +295,10 @@ impl UntypedComponentStore { entity: Entity, f: impl FnOnce() -> T, ) -> &mut T { - if self.bitset.bit_test(entity.index() as usize) { - return self.get_mut(entity).unwrap(); - } else { + if !self.bitset.bit_test(entity.index() as usize) { self.insert(entity, f()); - self.get_mut(entity).unwrap() } + self.get_mut(entity).unwrap() } /// Get a [`SchemaRefMut`] to the component for the given [`Entity`] @@ -492,6 +490,37 @@ impl UntypedComponentStore { } } + /// Get a reference to the component store if there is exactly one instance of the component. + pub fn get_single_with_bitset( + &self, + bitset: Rc, + ) -> Result { + let len = self.bitset().bit_len(); + let mut iter = (0..len).filter(|&i| bitset.bit_test(i) && self.bitset().bit_test(i)); + let i = iter.next().ok_or(QuerySingleError::NoEntities)?; + if iter.next().is_some() { + return Err(QuerySingleError::MultipleEntities); + } + // TODO: add unchecked variant to avoid redundant validation + self.get_idx(i).ok_or(QuerySingleError::NoEntities) + } + + /// Get a mutable reference to the component store if there is exactly one instance of the + /// component. + pub fn get_single_with_bitset_mut( + &mut self, + bitset: Rc, + ) -> Result { + let len = self.bitset().bit_len(); + let mut iter = (0..len).filter(|&i| bitset.bit_test(i) && self.bitset().bit_test(i)); + let i = iter.next().ok_or(QuerySingleError::NoEntities)?; + if iter.next().is_some() { + return Err(QuerySingleError::MultipleEntities); + } + // TODO: add unchecked variant to avoid redundant validation + self.get_idx_mut(i).ok_or(QuerySingleError::NoEntities) + } + /// Iterates immutably over all components of this type. /// /// Very fast but doesn't allow joining with other component types. @@ -600,9 +629,8 @@ impl<'a> Iterator for UntypedComponentStoreIter<'a> { if let Some(ptr) = self.store.get_idx(self.idx) { self.idx += 1; break Some(ptr); - } else { - self.idx += 1; } + self.idx += 1; } else { break None; } @@ -627,9 +655,8 @@ impl<'a> Iterator for UntypedComponentStoreIterMut<'a> { break Some(unsafe { SchemaRefMut::from_ptr_schema(ptr.as_ptr(), ptr.schema()) }); - } else { - self.idx += 1; } + self.idx += 1; } else { break None; } diff --git a/framework_crates/bones_ecs/src/entities.rs b/framework_crates/bones_ecs/src/entities.rs index d8b1f838b1..53eb3ff688 100644 --- a/framework_crates/bones_ecs/src/entities.rs +++ b/framework_crates/bones_ecs/src/entities.rs @@ -91,10 +91,28 @@ pub trait QueryItem { type Iter: Iterator; /// Modify the iteration bitset fn apply_bitset(&self, bitset: &mut BitSetVec); + /// Return the item that matches the query within the given bitset if there is exactly one + /// entity that matches this query item. + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError>; /// Return an iterator over the provided bitset. fn iter_with_bitset(self, bitset: Rc) -> Self::Iter; } +/// An error that may occur when querying for a single entity. For example, via +/// [`Entities::get_single_with`], or more directly with +/// [`ComponentStore::get_single_with_bitset`] or +/// [`ComponentStore::get_single_mut_with_bitset`]. +#[derive(Debug, PartialEq, Eq)] +pub enum QuerySingleError { + /// No entity matches the query. + NoEntities, + /// More than one entity matches the query. + MultipleEntities, +} + /// Wrapper for the [`Comp`] [`SystemParam`] used as [`QueryItem`] to iterate /// over entities optionally retrieving components from [`ComponentStore`]. /// Entities iterated over will not be filtered by [`OptionalQueryItem`]. @@ -153,10 +171,18 @@ where impl<'a> QueryItem for &'a Ref<'a, UntypedComponentStore> { type Iter = UntypedComponentBitsetIterator<'a>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + UntypedComponentStore::get_single_with_bitset(self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { UntypedComponentStore::iter_with_bitset(self, bitset) } @@ -164,10 +190,18 @@ impl<'a> QueryItem for &'a Ref<'a, UntypedComponentStore> { impl<'a, 'q, T: HasSchema> QueryItem for &'a Comp<'q, T> { type Iter = ComponentBitsetIterator<'a, T>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + ComponentStore::get_single_with_bitset(&**self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { ComponentStore::iter_with_bitset(&**self, bitset) } @@ -175,10 +209,18 @@ impl<'a, 'q, T: HasSchema> QueryItem for &'a Comp<'q, T> { impl<'a, 'q, T: HasSchema> QueryItem for &'a CompMut<'q, T> { type Iter = ComponentBitsetIterator<'a, T>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + ComponentStore::get_single_with_bitset(&**self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { ComponentStore::iter_with_bitset(&**self, bitset) } @@ -186,10 +228,18 @@ impl<'a, 'q, T: HasSchema> QueryItem for &'a CompMut<'q, T> { impl<'a, 'q, T: HasSchema> QueryItem for &'a mut CompMut<'q, T> { type Iter = ComponentBitsetIteratorMut<'a, T>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + ComponentStore::get_single_with_bitset_mut(self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { ComponentStore::iter_mut_with_bitset(self, bitset) } @@ -203,8 +253,20 @@ where S: std::ops::Deref + 'a, { type Iter = ComponentBitsetOptionalIterator<'a, T>; + fn apply_bitset(&self, _bitset: &mut BitSetVec) {} + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + match self.0.get_single_with_bitset(bitset) { + Ok(single) => Ok(Some(single)), + Err(QuerySingleError::NoEntities) => Ok(None), + Err(err) => Err(err), + } + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { self.0.iter_with_bitset_optional(bitset) } @@ -217,8 +279,20 @@ where S: std::ops::DerefMut + 'a, { type Iter = ComponentBitsetOptionalIteratorMut<'a, T>; + fn apply_bitset(&self, _bitset: &mut BitSetVec) {} + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + match self.0.get_single_mut_with_bitset(bitset) { + Ok(x) => Ok(Some(x)), + Err(QuerySingleError::NoEntities) => Ok(None), + Err(err) => Err(err), + } + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { self.0.iter_mut_with_bitset_optional(bitset) } @@ -298,6 +372,32 @@ macro_rules! impl_query { )* } + #[allow(non_snake_case)] + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + let ( + $( + $args, + )* + ) = self; + let mut query = MultiQueryIter { + data: ( + $( + $args.iter_with_bitset(bitset.clone()), + )* + ) + }; + let first = query.next(); + let has_second = query.next().is_some(); + match (first, has_second) { + (None, _) => Err(QuerySingleError::NoEntities), + (Some(items), false) => Ok(items), + (Some(_), true) => Err(QuerySingleError::MultipleEntities), + } + } + #[allow(non_snake_case)] fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { let ( @@ -363,6 +463,89 @@ impl<'a, I: Iterator> Iterator for EntitiesIterWith<'a, I> { } impl Entities { + /// Get a single entity and components in the given query if there is exactly one entity + /// matching the query. + /// + /// # Panics + /// + /// This method panics if the number of matching entities is not *exactly one*. + pub fn single_with( + &self, + query: Q, + ) -> (Entity, <::Iter as Iterator>::Item) { + self.get_single_with(query).unwrap() + } + + /// Get a single entity and components in the given query if there is exactly one entity + /// matching the query. + pub fn get_single_with( + &self, + query: Q, + ) -> Result<(Entity, <::Iter as Iterator>::Item), QuerySingleError> { + let mut bitset = self.bitset().clone(); + query.apply_bitset(&mut bitset); + + let entity = { + let mut ids = (0..self.next_id).filter(|&i| bitset.bit_test(i)); + let id = ids.next().ok_or(QuerySingleError::NoEntities)?; + if ids.next().is_some() { + return Err(QuerySingleError::MultipleEntities); + } + Entity::new(id as u32, self.generation[id]) + }; + + let bitset = Rc::new(bitset); + + query + .get_single_with_bitset(bitset) + .map(|item| (entity, item)) + } + + /// Get the first entity in the given bitset. + /// + /// # Panics + /// + /// This method panics if there are no entities in the bitset. + pub fn first_with_bitset(&self, bitset: &BitSetVec) -> Entity { + self.get_first_with_bitset(bitset).unwrap() + } + + /// Get the first entity in the given bitset. + pub fn get_first_with_bitset(&self, bitset: &BitSetVec) -> Option { + self.iter_with_bitset(bitset).next() + } + + /// Get the first entity and components in the given query. + /// + /// # Panics + /// + /// This method panics if there are no entities that match the query. + pub fn first_with( + &self, + query: Q, + ) -> (Entity, <::Iter as Iterator>::Item) { + self.get_first_with(query).unwrap() + } + + /// Get the first entity and components in the given query. + pub fn get_first_with( + &self, + query: Q, + ) -> Option<(Entity, <::Iter as Iterator>::Item)> { + self.iter_with(query).next() + } + + /// Iterates over entities using the provided bitset. + pub fn iter_with_bitset<'a>(&'a self, bitset: &'a BitSetVec) -> EntityIterator { + EntityIterator { + current_id: 0, + next_id: self.next_id, + entities: &self.alive, + generations: &self.generation, + bitset, + } + } + /// Iterate over the entities and components in the given query. /// /// The [`QueryItem`] trait is automatically implemented for references to [`Comp`] and @@ -531,17 +714,6 @@ impl Entities { &self.alive } - /// Iterates over entities using the provided bitset. - pub fn iter_with_bitset<'a>(&'a self, bitset: &'a BitSetVec) -> EntityIterator { - EntityIterator { - current_id: 0, - next_id: self.next_id, - entities: &self.alive, - generations: &self.generation, - bitset, - } - } - /// Iterates over all alive entities. pub fn iter(&self) -> EntityIterator { EntityIterator { @@ -587,12 +759,155 @@ impl<'a> Iterator for EntityIterator<'a> { #[cfg(test)] mod tests { - use std::collections::HashSet; + #![allow(non_snake_case)] + + use std::{collections::HashSet, rc::Rc}; use crate::prelude::*; + #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] + #[repr(C)] + struct A(String); + + #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] + #[repr(C)] + struct B(String); + #[test] - fn create_kill_entities() { + fn query_item__get_single_with_one_required() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + { + let comp = state.borrow_mut(); + let query = ∁ + + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::NoEntities)); + } + + { + let mut comp = state.borrow_mut(); + + let e = entities.create(); + let a = A("e".to_string()); + comp.insert(e, a.clone()); + + let query = ∁ + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Ok(&a)); + + entities.kill(e); + } + + { + let mut comp = state.borrow_mut(); + + let e1 = entities.create(); + comp.insert(e1, A("e1".to_string())); + + let e2 = entities.create(); + comp.insert(e2, A("e2".to_string())); + + let query = ∁ + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::MultipleEntities)); + + entities.kill(e1); + entities.kill(e2); + } + } + + #[test] + fn query_item__get_single_with_multiple_required() { + let mut entities = Entities::default(); + let state_a = AtomicCell::new(ComponentStore::::default()); + let state_b = AtomicCell::new(ComponentStore::::default()); + + { + let query = (&state_a.borrow(), &state_b.borrow()); + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::NoEntities)); + } + + { + let e = entities.create(); + let a = A("e".to_string()); + let b = B("e".to_string()); + state_a.borrow_mut().insert(e, a.clone()); + state_b.borrow_mut().insert(e, b.clone()); + + let query = (&state_a.borrow(), &state_b.borrow()); + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Ok((&a, &b))); + + entities.kill(e); + } + + { + let e1 = entities.create(); + state_a.borrow_mut().insert(e1, A("e1".to_string())); + state_b.borrow_mut().insert(e1, B("e1".to_string())); + + let e2 = entities.create(); + state_a.borrow_mut().insert(e2, A("e2".to_string())); + state_b.borrow_mut().insert(e2, B("e2".to_string())); + + let query = (&state_a.borrow(), &state_b.borrow()); + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::MultipleEntities)); + + entities.kill(e1); + entities.kill(e2); + } + } + + #[test] + fn query_item__get_single_with_bitset__uses_bitset() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + let e = entities.create(); + state.borrow_mut().insert(e, A("unexpected".to_string())); + + let query = &state.borrow(); + let bitset = Rc::new({ + let mut bitset = BitSetVec::default(); + bitset.bit_set(99); + bitset + }); + + let maybe_comp = query.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Err(QuerySingleError::NoEntities)); + } + + #[test] + fn entities__create_kill() { let mut entities = Entities::default(); let e1 = entities.create(); let e2 = entities.create(); @@ -620,7 +935,7 @@ mod tests { } #[test] - fn test_interleaved_create_kill() { + fn entities__interleaved_create_kill() { let mut entities = Entities::default(); let e1 = entities.create(); @@ -644,7 +959,7 @@ mod tests { #[test] /// Exercise basic operations on entities to increase code coverage - fn clone_debug_hash() { + fn entities__clone_debug_hash() { let mut entities = Entities::default(); let e1 = entities.create(); // Clone @@ -661,7 +976,7 @@ mod tests { /// /// Exercises a code path not tested according to code coverage. #[test] - fn force_generate_next_section() { + fn entities__force_generate_next_section() { let mut entities = Entities::default(); // Create enough entities to fil up the first section of the bitset for _ in 0..256 { @@ -678,7 +993,7 @@ mod tests { #[cfg(not(miri))] // This test is very slow on miri and not critical to test for. #[test] #[should_panic(expected = "Exceeded maximum amount")] - fn force_max_entity_panic() { + fn entities__force_max_entity_panic() { let mut entities = Entities::default(); for _ in 0..(BITSET_SIZE + 1) { entities.create(); @@ -688,7 +1003,7 @@ mod tests { #[cfg(not(miri))] // This test is very slow on miri and not critical to test for. #[test] #[should_panic(expected = "Exceeded maximum amount")] - fn force_max_entity_panic2() { + fn entities__force_max_entity_panic2() { let mut entities = Entities::default(); let mut e = None; for _ in 0..BITSET_SIZE { @@ -701,7 +1016,7 @@ mod tests { } #[test] - fn iter_with_empty_bitset() { + fn entities__iter_with_empty_bitset() { let mut entities = Entities::default(); // Create a couple entities @@ -712,4 +1027,173 @@ mod tests { let bitset = BitSetVec::default(); assert_eq!(entities.iter_with_bitset(&bitset).count(), 0); } + + #[test] + fn entities__get_single__with_one_required__ok() { + let mut entities = Entities::default(); + (0..3).map(|_| entities.create()).count(); + let e = entities.create(); + let a = A("a".to_string()); + + let state = AtomicCell::new(ComponentStore::::default()); + state.borrow_mut().insert(e, a.clone()); + + let comp = state.borrow(); + + assert_eq!(entities.get_single_with(&comp), Ok((e, &a))); + } + + #[test] + fn entities__get_single__with_one_required__none() { + let mut entities = Entities::default(); + (0..3).map(|_| entities.create()).count(); + + let state = AtomicCell::new(ComponentStore::::default()); + let comp = state.borrow(); + + assert_eq!( + entities.get_single_with(&comp), + Err(QuerySingleError::NoEntities) + ); + } + + #[test] + fn entities__get_single__with_one_required__too_many() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + for i in 0..3 { + let e = entities.create(); + let a = A(i.to_string()); + state.borrow_mut().insert(e, a.clone()); + } + + let comp = state.borrow(); + + assert_eq!( + entities.get_single_with(&comp), + Err(QuerySingleError::MultipleEntities) + ); + } + + #[test] + fn entities__get_single__with_multiple_required() { + let mut entities = Entities::default(); + + let state_a = AtomicCell::new(ComponentStore::::default()); + let state_b = AtomicCell::new(ComponentStore::::default()); + + let _e1 = entities.create(); + + let e2 = entities.create(); + state_a.borrow_mut().insert(e2, A("a2".to_string())); + + let e3 = entities.create(); + state_b.borrow_mut().insert(e3, B("b3".to_string())); + + let e4 = entities.create(); + let a4 = A("a4".to_string()); + let b4 = B("b4".to_string()); + state_a.borrow_mut().insert(e4, a4.clone()); + state_b.borrow_mut().insert(e4, b4.clone()); + + let comp_a = state_a.borrow(); + let comp_b = state_b.borrow(); + assert_eq!( + entities.get_single_with((&comp_a, &comp_b)), + Ok((e4, (&a4, &b4))) + ); + } + + #[test] + fn entities__get_single__with_one_optional() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + { + let e = entities.create(); + + let mut comp = state.borrow_mut(); + + assert_eq!(entities.get_single_with(&Optional(&comp)), Ok((e, None))); + + assert_eq!( + entities.get_single_with(&mut OptionalMut(&mut comp)), + Ok((e, None)) + ); + + entities.kill(e); + } + + { + let e = entities.create(); + let mut a = A("a".to_string()); + state.borrow_mut().insert(e, a.clone()); + + let mut comp = state.borrow_mut(); + + assert_eq!( + entities.get_single_with(&Optional(&comp)), + Ok((e, Some(&a))) + ); + + assert_eq!( + entities.get_single_with(&mut OptionalMut(&mut comp)), + Ok((e, Some(&mut a))) + ); + + entities.kill(e); + } + } + + #[test] + fn entities__get_single__with_required_and_optional() { + let mut entities = Entities::default(); + let state_a = AtomicCell::new(ComponentStore::::default()); + let state_b = AtomicCell::new(ComponentStore::::default()); + + { + let e = entities.create(); + let a = A("a".to_string()); + state_a.borrow_mut().insert(e, a.clone()); + + let comp_a = state_a.borrow(); + let mut comp_b = state_b.borrow_mut(); + + assert_eq!( + entities.get_single_with((&comp_a, &Optional(&comp_b))), + Ok((e, (&a, None))) + ); + + assert_eq!( + entities.get_single_with((&comp_a, &mut OptionalMut(&mut comp_b))), + Ok((e, (&a, None))) + ); + + entities.kill(e); + } + + { + let e = entities.create(); + let a = A("a".to_string()); + let mut b = B("b".to_string()); + state_a.borrow_mut().insert(e, a.clone()); + state_b.borrow_mut().insert(e, b.clone()); + + let comp_a = state_a.borrow(); + let mut comp_b = state_b.borrow_mut(); + + assert_eq!( + entities.get_single_with((&comp_a, &Optional(&comp_b))), + Ok((e, (&a, Some(&b)))) + ); + + assert_eq!( + entities.get_single_with((&comp_a, &mut OptionalMut(&mut comp_b))), + Ok((e, (&a, Some(&mut b)))) + ); + + entities.kill(e); + } + } }