1#![forbid(unsafe_code)]
36
37use std::any::{Any, TypeId, type_name};
38use std::cell::RefCell;
39pub use std::cell::{Ref, RefMut};
40use std::collections::HashMap;
41use std::fmt;
42use std::marker::PhantomData;
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
47pub struct Entity {
48 index: u32,
49 generation: u32,
50}
51
52impl Entity {
53 pub fn id(self) -> u32 {
55 self.index
56 }
57 pub fn generation(self) -> u32 {
58 self.generation
59 }
60 pub fn to_bits(self) -> u64 {
62 (u64::from(self.generation) << 32) | u64::from(self.index)
63 }
64 pub fn from_bits(bits: u64) -> Self {
66 Self {
67 index: bits as u32,
68 generation: (bits >> 32) as u32,
69 }
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
74pub enum Error {
75 NoSuchEntity(Entity),
76 MissingComponent(&'static str),
77 BorrowConflict(&'static str),
78 DuplicateComponent(&'static str),
79 ConflictingQuery(&'static str),
80}
81
82impl fmt::Display for Error {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 match self {
85 Self::NoSuchEntity(entity) => write!(f, "entity {entity:?} is not alive"),
86 Self::MissingComponent(name) => write!(f, "entity has no {name} component"),
87 Self::BorrowConflict(name) => {
88 write!(f, "{name} component is already incompatibly borrowed")
89 }
90 Self::DuplicateComponent(name) => write!(f, "bundle contains {name} more than once"),
91 Self::ConflictingQuery(name) => {
92 write!(f, "query requests overlapping mutable access to {name}")
93 }
94 }
95 }
96}
97impl std::error::Error for Error {}
98
99#[derive(Default)]
100struct Slot {
101 generation: u32,
102 alive: bool,
103}
104
105trait ErasedStorage {
106 fn as_any(&self) -> &dyn Any;
107 fn as_any_mut(&mut self) -> &mut dyn Any;
108 fn erase(&mut self, index: usize);
109}
110
111struct Storage<T> {
112 slots: Vec<Option<RefCell<T>>>,
113}
114
115impl<T> Default for Storage<T> {
116 fn default() -> Self {
117 Self { slots: Vec::new() }
118 }
119}
120
121impl<T: 'static> ErasedStorage for Storage<T> {
122 fn as_any(&self) -> &dyn Any {
123 self
124 }
125 fn as_any_mut(&mut self) -> &mut dyn Any {
126 self
127 }
128 fn erase(&mut self, index: usize) {
129 if let Some(slot) = self.slots.get_mut(index) {
130 *slot = None;
131 }
132 }
133}
134
135#[derive(Default)]
140pub struct World {
141 slots: Vec<Slot>,
142 free: Vec<u32>,
143 storages: HashMap<TypeId, Box<dyn ErasedStorage>>,
144 len: usize,
145}
146
147impl World {
148 pub fn new() -> Self {
149 Self::default()
150 }
151 pub fn len(&self) -> usize {
152 self.len
153 }
154 pub fn is_empty(&self) -> bool {
155 self.len == 0
156 }
157
158 pub fn contains(&self, entity: Entity) -> bool {
159 self.slots
160 .get(entity.index as usize)
161 .is_some_and(|slot| slot.alive && slot.generation == entity.generation)
162 }
163
164 pub fn spawn<B: Bundle>(&mut self, bundle: B) -> Entity {
167 self.try_spawn(bundle)
168 .unwrap_or_else(|error| panic!("{error}"))
169 }
170
171 pub fn try_spawn<B: Bundle>(&mut self, bundle: B) -> Result<Entity, Error> {
172 validate_bundle::<B>()?;
173 let index = if let Some(index) = self.free.pop() {
174 index
175 } else {
176 let index = u32::try_from(self.slots.len()).expect("entity slot capacity exceeded");
177 self.slots.push(Slot::default());
178 index
179 };
180 let slot = &mut self.slots[index as usize];
181 slot.alive = true;
182 let entity = Entity {
183 index,
184 generation: slot.generation,
185 };
186 self.len += 1;
187 bundle.write(self, index as usize);
188 Ok(entity)
189 }
190
191 pub fn despawn(&mut self, entity: Entity) -> Result<(), Error> {
193 self.validate(entity)?;
194 let slot = &mut self.slots[entity.index as usize];
195 slot.alive = false;
196 self.len -= 1;
197 let next_generation = slot.generation.checked_add(1);
199 if let Some(next) = next_generation {
200 slot.generation = next;
201 }
202 for storage in self.storages.values_mut() {
203 storage.erase(entity.index as usize);
204 }
205 if next_generation.is_some() {
208 self.free.push(entity.index);
209 }
210 Ok(())
211 }
212
213 pub fn insert<B: Bundle>(&mut self, entity: Entity, bundle: B) -> Result<(), Error> {
216 self.validate(entity)?;
217 validate_bundle::<B>()?;
218 bundle.write(self, entity.index as usize);
219 Ok(())
220 }
221
222 pub fn remove<T: 'static>(&mut self, entity: Entity) -> Result<T, Error> {
223 self.validate(entity)?;
224 self.storages
225 .get_mut(&TypeId::of::<T>())
226 .and_then(|s| s.as_any_mut().downcast_mut::<Storage<T>>())
227 .and_then(|storage| storage.slots.get_mut(entity.index as usize))
228 .and_then(Option::take)
229 .map(RefCell::into_inner)
230 .ok_or(Error::MissingComponent(type_name::<T>()))
231 }
232
233 pub fn has<T: 'static>(&self, entity: Entity) -> bool {
234 self.contains(entity) && self.cell::<T>(entity.index as usize).is_some()
235 }
236
237 pub fn get<A: Access>(&self, entity: Entity) -> Result<A::Item<'_>, Error> {
240 self.validate(entity)?;
241 A::fetch(self, entity)
242 }
243
244 pub fn get_mut<T: 'static>(&self, entity: Entity) -> Result<RefMut<'_, T>, Error> {
245 self.get::<&mut T>(entity)
246 }
247
248 pub fn query<Q: Query>(&self) -> QueryBorrow<'_, Q> {
251 self.try_query().unwrap_or_else(|error| panic!("{error}"))
252 }
253
254 pub fn try_query<Q: Query>(&self) -> Result<QueryBorrow<'_, Q>, Error> {
255 Q::validate()?;
256 Ok(QueryBorrow {
257 world: self,
258 marker: PhantomData,
259 })
260 }
261
262 pub fn entities(&self) -> impl Iterator<Item = Entity> + '_ {
264 self.slots.iter().enumerate().filter_map(|(index, slot)| {
265 slot.alive.then_some(Entity {
266 index: index as u32,
267 generation: slot.generation,
268 })
269 })
270 }
271
272 fn validate(&self, entity: Entity) -> Result<(), Error> {
273 if self.contains(entity) {
274 Ok(())
275 } else {
276 Err(Error::NoSuchEntity(entity))
277 }
278 }
279
280 fn cell<T: 'static>(&self, index: usize) -> Option<&RefCell<T>> {
281 self.storages
282 .get(&TypeId::of::<T>())
283 .and_then(|s| s.as_any().downcast_ref::<Storage<T>>())
284 .and_then(|storage| storage.slots.get(index))
285 .and_then(Option::as_ref)
286 }
287
288 fn put<T: 'static>(&mut self, index: usize, value: T) {
289 let storage = self
290 .storages
291 .entry(TypeId::of::<T>())
292 .or_insert_with(|| Box::<Storage<T>>::default())
293 .as_any_mut()
294 .downcast_mut::<Storage<T>>()
295 .expect("component storage type matches its TypeId");
296 if storage.slots.len() <= index {
297 storage.slots.resize_with(index + 1, || None);
298 }
299 storage.slots[index] = Some(RefCell::new(value));
300 }
301}
302
303mod sealed {
304 pub trait Bundle {}
305 pub trait Access {}
306 pub trait Query {}
307}
308
309pub trait Bundle: sealed::Bundle {
312 #[doc(hidden)]
313 fn component_types(types: &mut Vec<(TypeId, &'static str)>);
314 #[doc(hidden)]
315 fn write(self, world: &mut World, index: usize);
316}
317
318impl sealed::Bundle for () {}
319impl Bundle for () {
320 fn component_types(_: &mut Vec<(TypeId, &'static str)>) {}
321 fn write(self, _: &mut World, _: usize) {}
322}
323
324macro_rules! bundle {
325 ($($name:ident : $index:tt),+) => {
326 impl<$($name: 'static),+> sealed::Bundle for ($($name,)+) {}
327 impl<$($name: 'static),+> Bundle for ($($name,)+) {
328 fn component_types(types: &mut Vec<(TypeId, &'static str)>) {
329 $(types.push((TypeId::of::<$name>(), type_name::<$name>()));)+
330 }
331 fn write(self, world: &mut World, index: usize) {
332 $(world.put(index, self.$index);)+
333 }
334 }
335 }
336}
337bundle!(A: 0);
338bundle!(A: 0, B: 1);
339bundle!(A: 0, B: 1, C: 2);
340bundle!(A: 0, B: 1, C: 2, D: 3);
341
342fn validate_bundle<B: Bundle>() -> Result<(), Error> {
343 let mut types = Vec::new();
344 B::component_types(&mut types);
345 for (index, (id, name)) in types.iter().enumerate() {
346 if types[..index].iter().any(|other| other.0 == *id) {
347 return Err(Error::DuplicateComponent(name));
348 }
349 }
350 Ok(())
351}
352
353#[doc(hidden)]
354pub struct AccessInfo {
355 id: TypeId,
356 name: &'static str,
357 mutable: bool,
358}
359
360pub trait Access: sealed::Access {
362 type Item<'world>;
363 #[doc(hidden)]
364 fn info() -> Option<AccessInfo>;
365 #[doc(hidden)]
366 fn matches(world: &World, entity: Entity) -> bool;
367 #[doc(hidden)]
368 fn fetch(world: &World, entity: Entity) -> Result<Self::Item<'_>, Error>;
369}
370
371impl<T: 'static> sealed::Access for &T {}
372impl<T: 'static> Access for &T {
373 type Item<'world> = Ref<'world, T>;
374 fn info() -> Option<AccessInfo> {
375 Some(AccessInfo {
376 id: TypeId::of::<T>(),
377 name: type_name::<T>(),
378 mutable: false,
379 })
380 }
381 fn matches(world: &World, entity: Entity) -> bool {
382 world.has::<T>(entity)
383 }
384 fn fetch(world: &World, entity: Entity) -> Result<Self::Item<'_>, Error> {
385 world.validate(entity)?;
386 world
387 .cell::<T>(entity.index as usize)
388 .ok_or(Error::MissingComponent(type_name::<T>()))?
389 .try_borrow()
390 .map_err(|_| Error::BorrowConflict(type_name::<T>()))
391 }
392}
393impl<T: 'static> sealed::Access for &mut T {}
394impl<T: 'static> Access for &mut T {
395 type Item<'world> = RefMut<'world, T>;
396 fn info() -> Option<AccessInfo> {
397 Some(AccessInfo {
398 id: TypeId::of::<T>(),
399 name: type_name::<T>(),
400 mutable: true,
401 })
402 }
403 fn matches(world: &World, entity: Entity) -> bool {
404 world.has::<T>(entity)
405 }
406 fn fetch(world: &World, entity: Entity) -> Result<Self::Item<'_>, Error> {
407 world.validate(entity)?;
408 world
409 .cell::<T>(entity.index as usize)
410 .ok_or(Error::MissingComponent(type_name::<T>()))?
411 .try_borrow_mut()
412 .map_err(|_| Error::BorrowConflict(type_name::<T>()))
413 }
414}
415impl sealed::Access for Entity {}
416impl Access for Entity {
417 type Item<'world> = Entity;
418 fn info() -> Option<AccessInfo> {
419 None
420 }
421 fn matches(world: &World, entity: Entity) -> bool {
422 world.contains(entity)
423 }
424 fn fetch(world: &World, entity: Entity) -> Result<Self::Item<'_>, Error> {
425 world.validate(entity)?;
426 Ok(entity)
427 }
428}
429
430pub trait Query: sealed::Query {
432 type Item<'world>;
433 #[doc(hidden)]
434 fn validate() -> Result<(), Error>;
435 #[doc(hidden)]
436 fn matches(world: &World, entity: Entity) -> bool;
437 #[doc(hidden)]
438 fn fetch(world: &World, entity: Entity) -> Result<Self::Item<'_>, Error>;
439}
440
441impl<A: Access> sealed::Query for A {}
442impl<A: Access> Query for A {
443 type Item<'world> = A::Item<'world>;
444 fn validate() -> Result<(), Error> {
445 Ok(())
446 }
447 fn matches(world: &World, entity: Entity) -> bool {
448 A::matches(world, entity)
449 }
450 fn fetch(world: &World, entity: Entity) -> Result<Self::Item<'_>, Error> {
451 A::fetch(world, entity)
452 }
453}
454
455macro_rules! query_tuple {
456 ($($name:ident),+) => {
457 impl<$($name: Access),+> sealed::Query for ($($name,)+) {}
458 impl<$($name: Access),+> Query for ($($name,)+) {
459 type Item<'world> = ($($name::Item<'world>,)+);
460 fn validate() -> Result<(), Error> {
461 let access = [$($name::info(),)+];
462 for (index, item) in access.iter().enumerate() {
463 if let Some(item) = item {
464 if access[..index].iter().flatten().any(|other| {
465 other.id == item.id && (other.mutable || item.mutable)
466 }) { return Err(Error::ConflictingQuery(item.name)); }
467 }
468 }
469 Ok(())
470 }
471 fn matches(world: &World, entity: Entity) -> bool {
472 $($name::matches(world, entity))&&+
473 }
474 fn fetch(world: &World, entity: Entity) -> Result<Self::Item<'_>, Error> {
475 Ok(($($name::fetch(world, entity)?,)+))
476 }
477 }
478 }
479}
480query_tuple!(A);
481query_tuple!(A, B);
482query_tuple!(A, B, C);
483query_tuple!(A, B, C, D);
484
485pub struct QueryBorrow<'world, Q: Query> {
486 world: &'world World,
487 marker: PhantomData<fn() -> Q>,
488}
489
490impl<'world, Q: Query> QueryBorrow<'world, Q> {
491 pub fn iter(&self) -> QueryIter<'world, Q> {
492 QueryIter {
493 inner: self.try_iter(),
494 }
495 }
496 pub fn try_iter(&self) -> TryQueryIter<'world, Q> {
499 TryQueryIter {
500 world: self.world,
501 cursor: 0,
502 marker: PhantomData,
503 }
504 }
505}
506impl<'world, Q: Query> IntoIterator for &mut QueryBorrow<'world, Q> {
507 type Item = Q::Item<'world>;
508 type IntoIter = QueryIter<'world, Q>;
509 fn into_iter(self) -> Self::IntoIter {
510 self.iter()
511 }
512}
513impl<'world, Q: Query> IntoIterator for &QueryBorrow<'world, Q> {
514 type Item = Q::Item<'world>;
515 type IntoIter = QueryIter<'world, Q>;
516 fn into_iter(self) -> Self::IntoIter {
517 self.iter()
518 }
519}
520impl<'world, Q: Query> IntoIterator for QueryBorrow<'world, Q> {
521 type Item = Q::Item<'world>;
522 type IntoIter = QueryIter<'world, Q>;
523 fn into_iter(self) -> Self::IntoIter {
524 self.iter()
525 }
526}
527
528pub struct TryQueryIter<'world, Q: Query> {
529 world: &'world World,
530 cursor: usize,
531 marker: PhantomData<fn() -> Q>,
532}
533impl<'world, Q: Query> Iterator for TryQueryIter<'world, Q> {
534 type Item = Result<Q::Item<'world>, Error>;
535 fn next(&mut self) -> Option<Self::Item> {
536 while let Some(slot) = self.world.slots.get(self.cursor) {
537 let entity = Entity {
538 index: self.cursor as u32,
539 generation: slot.generation,
540 };
541 self.cursor += 1;
542 if slot.alive && Q::matches(self.world, entity) {
544 return Some(Q::fetch(self.world, entity));
545 }
546 }
547 None
548 }
549 fn size_hint(&self) -> (usize, Option<usize>) {
550 (0, Some(self.world.slots.len().saturating_sub(self.cursor)))
551 }
552}
553impl<Q: Query> std::iter::FusedIterator for TryQueryIter<'_, Q> {}
554
555pub struct QueryIter<'world, Q: Query> {
556 inner: TryQueryIter<'world, Q>,
557}
558impl<'world, Q: Query> Iterator for QueryIter<'world, Q> {
559 type Item = Q::Item<'world>;
560 fn next(&mut self) -> Option<Self::Item> {
561 self.inner
562 .next()
563 .map(|item| item.unwrap_or_else(|error| panic!("{error}")))
564 }
565 fn size_hint(&self) -> (usize, Option<usize>) {
566 self.inner.size_hint()
567 }
568}
569impl<Q: Query> std::iter::FusedIterator for QueryIter<'_, Q> {}
570
571#[cfg(test)]
572mod tests {
573 use super::*;
574 use std::cell::Cell;
575 use std::rc::Rc;
576
577 #[derive(Debug, PartialEq)]
578 struct Position(f32, f32);
579 #[derive(Debug, PartialEq)]
580 struct Velocity(f32, f32);
581 #[derive(Debug, PartialEq)]
582 struct Health(i32);
583
584 #[test]
585 fn reused_slot_rejects_every_stale_handle_operation() {
586 let mut world = World::new();
587 let old = world.spawn((Position(1.0, 2.0), Health(10)));
588 world.despawn(old).unwrap();
589 let new = world.spawn((Position(8.0, 9.0),));
590 assert_eq!(old.id(), new.id());
591 assert_ne!(old.generation(), new.generation());
592 assert!(!world.contains(old));
593 assert!(world.contains(new));
594 assert_eq!(Entity::from_bits(new.to_bits()), new);
595 assert!(matches!(
596 world.get::<&Position>(old),
597 Err(Error::NoSuchEntity(_))
598 ));
599 assert!(matches!(
600 world.get_mut::<Position>(old),
601 Err(Error::NoSuchEntity(_))
602 ));
603 assert!(matches!(
604 world.insert(old, (Health(50),)),
605 Err(Error::NoSuchEntity(_))
606 ));
607 assert!(matches!(
608 world.remove::<Position>(old),
609 Err(Error::NoSuchEntity(_))
610 ));
611 assert!(matches!(world.despawn(old), Err(Error::NoSuchEntity(_))));
612 assert!(!world.has::<Health>(new));
613 assert_eq!(*world.get::<&Position>(new).unwrap(), Position(8.0, 9.0));
614 assert_eq!(world.len(), 1);
615 }
616
617 #[test]
618 fn exhausted_generations_retire_slots_instead_of_wrapping() {
619 let mut world = World::new();
620 let first = world.spawn((7_i32,));
621 world.slots[first.id() as usize].generation = u32::MAX;
622 let final_handle = Entity {
623 index: first.id(),
624 generation: u32::MAX,
625 };
626 world.despawn(final_handle).unwrap();
627 let next = world.spawn((8_i32,));
628 assert_ne!(next.id(), first.id());
629 assert!(!world.contains(first));
630 assert!(!world.contains(final_handle));
631 }
632
633 #[test]
634 fn tuple_queries_update_only_matching_entities_and_return_ids() {
635 let mut world = World::new();
636 let matching = world.spawn((Position(2.0, 3.0), Velocity(4.0, -1.0), Health(30), "pilot"));
637 let stationary = world.spawn((Position(20.0, 30.0), Health(100)));
638 let removed = world.spawn((Position(9.0, 9.0), Velocity(1.0, 1.0)));
639 world.despawn(removed).unwrap();
640 for (entity, mut position, mut velocity) in
641 &mut world.query::<(Entity, &mut Position, &mut Velocity)>()
642 {
643 assert_eq!(entity, matching);
644 position.0 += velocity.0;
645 position.1 += velocity.1;
646 velocity.0 *= 0.5;
647 }
648 assert_eq!(
649 *world.get::<&Position>(matching).unwrap(),
650 Position(6.0, 2.0)
651 );
652 assert_eq!(
653 *world.get::<&Velocity>(matching).unwrap(),
654 Velocity(2.0, -1.0)
655 );
656 assert_eq!(
657 *world.get::<&Position>(stationary).unwrap(),
658 Position(20.0, 30.0)
659 );
660 assert_eq!(
661 world
662 .query::<(&Position, &Velocity, &Health, &&str)>()
663 .iter()
664 .count(),
665 1
666 );
667 assert_eq!(world.query::<&Position>().iter().count(), 2);
668 assert_eq!(
669 world.entities().collect::<Vec<_>>(),
670 vec![matching, stationary]
671 );
672 }
673
674 #[test]
675 fn guards_can_outlive_iterators_and_other_entity_guards() {
676 let mut world = World::new();
677 let a = world.spawn((Health(1),));
678 let b = world.spawn((Health(2),));
679 let (mut first, mut second) = {
680 let mut items = world.query::<&mut Health>().iter();
681 let first = items.next().unwrap();
682 let second = items.next().unwrap();
683 assert!(items.next().is_none());
684 (first, second)
685 };
686 first.0 += 10;
687 second.0 += 20;
688 assert!(matches!(
689 world.get::<&Health>(a),
690 Err(Error::BorrowConflict(_))
691 ));
692 drop(first);
693 assert_eq!(world.get::<&Health>(a).unwrap().0, 11);
694 assert!(matches!(
695 world.get::<&Health>(b),
696 Err(Error::BorrowConflict(_))
697 ));
698 drop(second);
699 assert_eq!(world.get::<&Health>(b).unwrap().0, 22);
700 }
701
702 #[test]
703 fn borrow_conflicts_are_fallible_and_release_on_drop() {
704 let mut world = World::new();
705 let entity = world.spawn((Health(7), Position(2.0, 3.0)));
706 let shared = world.get::<&Health>(entity).unwrap();
707 let other_shared = world.get::<&Health>(entity).unwrap();
708 assert!(matches!(
709 world.get::<&mut Health>(entity),
710 Err(Error::BorrowConflict(_))
711 ));
712 assert!(world.get_mut::<Position>(entity).is_ok());
713 drop((shared, other_shared));
714 let mut exclusive = world.get_mut::<Health>(entity).unwrap();
715 exclusive.0 = 9;
716 assert!(matches!(
717 world.get::<&Health>(entity),
718 Err(Error::BorrowConflict(_))
719 ));
720 assert!(matches!(
721 world.get_mut::<Health>(entity),
722 Err(Error::BorrowConflict(_))
723 ));
724 drop(exclusive);
725 assert_eq!(world.get::<&Health>(entity).unwrap().0, 9);
726 }
727
728 #[test]
729 fn query_aliases_reject_mutable_duplicates_allow_shared_duplicates() {
730 let mut world = World::new();
731 world.spawn((Health(10),));
732 assert!(matches!(
733 world.try_query::<(&mut Health, &Health)>(),
734 Err(Error::ConflictingQuery(_))
735 ));
736 assert!(matches!(
737 world.try_query::<(&mut Health, &mut Health)>(),
738 Err(Error::ConflictingQuery(_))
739 ));
740 let values = world.query::<(&Health, &Health)>().iter().next().unwrap();
741 assert_eq!(values.0.0, values.1.0);
742 assert_eq!(world.query::<(Entity, Entity)>().iter().count(), 1);
743 }
744
745 #[test]
746 fn tuple_borrow_failure_releases_partial_borrows_and_continues() {
747 let mut world = World::new();
748 let first = world.spawn((Health(1), Position(1.0, 1.0)));
749 let second = world.spawn((Health(2), Position(2.0, 2.0)));
750 let lock = world.get_mut::<Position>(first).unwrap();
751 let mut iterator = world.query::<(&mut Health, &mut Position)>().try_iter();
752 assert!(matches!(
753 iterator.next().unwrap(),
754 Err(Error::BorrowConflict(_))
755 ));
756 assert!(world.get_mut::<Health>(first).is_ok());
758 let (mut health, _) = iterator.next().unwrap().unwrap();
759 health.0 = 12;
760 drop((iterator, health, lock));
761 assert_eq!(world.get::<&Health>(second).unwrap().0, 12);
762 }
763
764 #[test]
765 fn missing_components_are_filtered_before_any_borrow_attempt() {
766 let mut world = World::new();
767 let entity = world.spawn((Health(10),));
768 let guard = world.get_mut::<Health>(entity).unwrap();
769 let mut query = world.query::<(&mut Health, &Position)>().try_iter();
770 assert!(query.next().is_none());
771 drop(guard);
772 }
773
774 #[test]
775 fn duplicate_bundles_are_rejected_before_mutation() {
776 let mut world = World::new();
777 assert!(matches!(
778 world.try_spawn((1_i32, 2_i32)),
779 Err(Error::DuplicateComponent(_))
780 ));
781 assert!(world.is_empty());
782 let entity = world.spawn((Health(10), 5_i32));
783 assert!(matches!(
784 world.insert(entity, (6_i32, 7_i32)),
785 Err(Error::DuplicateComponent(_))
786 ));
787 assert_eq!(*world.get::<&i32>(entity).unwrap(), 5);
788 assert_eq!(world.len(), 1);
789 }
790
791 #[test]
792 fn insert_replace_remove_and_empty_entities_work() {
793 let mut world = World::new();
794 let entity = world.spawn(());
795 assert!(world.contains(entity));
796 world
797 .insert(entity, (Health(10), Position(1.0, 2.0)))
798 .unwrap();
799 world.insert(entity, (Health(25),)).unwrap();
800 assert_eq!(world.remove::<Health>(entity).unwrap(), Health(25));
801 assert!(matches!(
802 world.remove::<Health>(entity),
803 Err(Error::MissingComponent(_))
804 ));
805 assert!(matches!(
806 world.get::<&Health>(entity),
807 Err(Error::MissingComponent(_))
808 ));
809 assert_eq!(world.query::<&Position>().iter().count(), 1);
810 world.despawn(entity).unwrap();
811 assert!(world.is_empty());
812 }
813
814 #[test]
815 fn components_drop_exactly_once_when_replaced_removed_or_despawned() {
816 struct CountDrop(Rc<Cell<usize>>);
817 impl Drop for CountDrop {
818 fn drop(&mut self) {
819 self.0.set(self.0.get() + 1);
820 }
821 }
822 let drops = Rc::new(Cell::new(0));
823 let mut world = World::new();
824 let entity = world.spawn((CountDrop(drops.clone()),));
825 world.insert(entity, (CountDrop(drops.clone()),)).unwrap();
826 assert_eq!(drops.get(), 1);
827 let removed = world.remove::<CountDrop>(entity).unwrap();
828 assert_eq!(drops.get(), 1);
829 drop(removed);
830 assert_eq!(drops.get(), 2);
831 world.insert(entity, (CountDrop(drops.clone()),)).unwrap();
832 world.despawn(entity).unwrap();
833 assert_eq!(drops.get(), 3);
834 drop(world);
835 assert_eq!(drops.get(), 3);
836 }
837
838 #[test]
839 fn independently_overlapping_iterators_conflict_without_aliasing() {
840 let mut world = World::new();
841 world.spawn((Health(10),));
842 let mut first = world.query::<&mut Health>().iter();
843 let guard = first.next().unwrap();
844 let mut second = world.query::<&mut Health>().try_iter();
845 assert!(matches!(
846 second.next().unwrap(),
847 Err(Error::BorrowConflict(_))
848 ));
849 drop(guard);
850 assert_eq!(world.query::<&Health>().iter().next().unwrap().0, 10);
851 }
852
853 #[test]
854 fn failed_panicking_query_does_not_poison_component_borrows() {
855 let mut world = World::new();
856 let entity = world.spawn((Health(10), Position(1.0, 2.0)));
857 let lock = world.get_mut::<Position>(entity).unwrap();
858 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
859 let _ = world.query::<(&mut Health, &mut Position)>().iter().next();
860 }));
861 assert!(result.is_err());
862 assert!(world.get_mut::<Health>(entity).is_ok());
863 drop(lock);
864 assert!(world.get_mut::<Position>(entity).is_ok());
865 }
866
867 #[test]
868 fn thousands_of_reuse_cycles_preserve_counts_and_components() {
869 let mut world = World::new();
870 let mut stale = Vec::new();
871 for i in 0_u32..2_000 {
872 let entity = world.spawn((i,));
873 assert_eq!(world.len(), 1);
874 assert_eq!(*world.get::<&u32>(entity).unwrap(), i);
875 assert!(stale.iter().all(|old| !world.contains(*old)));
876 world.despawn(entity).unwrap();
877 stale.push(entity);
878 assert!(world.is_empty());
879 }
880 assert_eq!(world.slots.len(), 1);
881 }
882 #[test]
883 fn panicking_destructor_does_not_recycle_partially_cleaned_slot() {
884 struct PanicOnDrop;
885 impl Drop for PanicOnDrop {
886 fn drop(&mut self) {
887 panic!("component destructor failed");
888 }
889 }
890 let mut world = World::new();
891 let entity = world.spawn((PanicOnDrop, Health(10)));
892 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
893 world.despawn(entity).unwrap();
894 }));
895 assert!(result.is_err());
896 assert!(!world.contains(entity));
897 assert!(world.is_empty());
898 let next = world.spawn((Position(1.0, 2.0),));
899 assert_ne!(next.id(), entity.id());
900 assert!(!world.has::<Health>(next));
901 }
902}