Add EntityDoesNotExistError, replace cases of Entity as an error, do some easy Resultification (#17855)

## Objective
There's no general error for when an entity doesn't exist, and some
methods are going to need one when they get Resultified. The closest
thing is `EntityFetchError`, but that error has a slightly more specific
purpose.

## Solution
- Added `EntityDoesNotExistError`.
  - Contains `Entity` and `EntityDoesNotExistDetails`.
- Changed `EntityFetchError` and `QueryEntityError`:
- Changed `NoSuchEntity` variant to wrap `EntityDoesNotExistError` and
renamed the variant to `EntityDoesNotExist`.
- Renamed `EntityFetchError` to `EntityMutableFetchError` to make its
purpose clearer.
- Renamed `TryDespawnError` to `EntityDespawnError` to make it more
general.
- Changed `World::inspect_entity` to return `Result<[ok],
EntityDoesNotExistError>` instead of panicking.
- Changed `World::get_entity` and `WorldEntityFetch::fetch_ref` to
return `Result<[ok], EntityDoesNotExistError>` instead of `Result<[ok],
Entity>`.
- Changed `UnsafeWorldCell::get_entity` to return
`Result<UnsafeEntityCell, EntityDoesNotExistError>` instead of
`Option<UnsafeEntityCell>`.

## Migration Guide
- `World::inspect_entity` now returns `Result<impl Iterator<Item =
&ComponentInfo>, EntityDoesNotExistError>` instead of `impl
Iterator<Item = &ComponentInfo>`.
- `World::get_entity` now returns `EntityDoesNotExistError` as an error
instead of `Entity`. You can still access the entity's ID through the
error's `entity` field.
- `UnsafeWorldCell::get_entity` now returns `Result<UnsafeEntityCell,
EntityDoesNotExistError>` instead of `Option<UnsafeEntityCell>`.
This commit is contained in:
JaySpruce 2025-02-16 15:59:46 -06:00 committed by GitHub
parent e186c7ccf4
commit ee44560523
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 212 additions and 207 deletions

View File

@ -1013,13 +1013,32 @@ impl Entities {
.map(Option::flatten) .map(Option::flatten)
} }
/// Constructs a message explaining why an entity does not exists, if known. /// Constructs a message explaining why an entity does not exist, if known.
pub(crate) fn entity_does_not_exist_error_details( pub(crate) fn entity_does_not_exist_error_details(
&self, &self,
_entity: Entity, entity: Entity,
) -> EntityDoesNotExistDetails { ) -> EntityDoesNotExistDetails {
EntityDoesNotExistDetails { EntityDoesNotExistDetails {
location: self.entity_get_spawned_or_despawned_by(_entity), location: self.entity_get_spawned_or_despawned_by(entity),
}
}
}
/// An error that occurs when a specified [`Entity`] does not exist.
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
#[error("The entity with ID {entity} {details}")]
pub struct EntityDoesNotExistError {
/// The entity's ID.
pub entity: Entity,
/// Details on why the entity does not exist, if available.
pub details: EntityDoesNotExistDetails,
}
impl EntityDoesNotExistError {
pub(crate) fn new(entity: Entity, entities: &Entities) -> Self {
Self {
entity,
details: entities.entity_does_not_exist_error_details(entity),
} }
} }
} }

View File

@ -1,7 +1,7 @@
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
entity::{Entity, EntityDoesNotExistDetails}, entity::{Entity, EntityDoesNotExistError},
world::unsafe_world_cell::UnsafeWorldCell, world::unsafe_world_cell::UnsafeWorldCell,
}; };
@ -14,13 +14,19 @@ pub enum QueryEntityError<'w> {
/// Either it does not have a requested component, or it has a component which the query filters out. /// Either it does not have a requested component, or it has a component which the query filters out.
QueryDoesNotMatch(Entity, UnsafeWorldCell<'w>), QueryDoesNotMatch(Entity, UnsafeWorldCell<'w>),
/// The given [`Entity`] does not exist. /// The given [`Entity`] does not exist.
NoSuchEntity(Entity, EntityDoesNotExistDetails), EntityDoesNotExist(EntityDoesNotExistError),
/// The [`Entity`] was requested mutably more than once. /// The [`Entity`] was requested mutably more than once.
/// ///
/// See [`Query::get_many_mut`](crate::system::Query::get_many_mut) for an example. /// See [`Query::get_many_mut`](crate::system::Query::get_many_mut) for an example.
AliasedMutability(Entity), AliasedMutability(Entity),
} }
impl<'w> From<EntityDoesNotExistError> for QueryEntityError<'w> {
fn from(error: EntityDoesNotExistError) -> Self {
QueryEntityError::EntityDoesNotExist(error)
}
}
impl<'w> core::error::Error for QueryEntityError<'w> {} impl<'w> core::error::Error for QueryEntityError<'w> {}
impl<'w> core::fmt::Display for QueryEntityError<'w> { impl<'w> core::fmt::Display for QueryEntityError<'w> {
@ -33,8 +39,8 @@ impl<'w> core::fmt::Display for QueryEntityError<'w> {
)?; )?;
format_archetype(f, world, entity) format_archetype(f, world, entity)
} }
Self::NoSuchEntity(entity, details) => { Self::EntityDoesNotExist(error) => {
write!(f, "The entity with ID {entity} {details}") write!(f, "{error}")
} }
Self::AliasedMutability(entity) => { Self::AliasedMutability(entity) => {
write!( write!(
@ -54,8 +60,8 @@ impl<'w> core::fmt::Debug for QueryEntityError<'w> {
format_archetype(f, world, entity)?; format_archetype(f, world, entity)?;
write!(f, ")") write!(f, ")")
} }
Self::NoSuchEntity(entity, details) => { Self::EntityDoesNotExist(error) => {
write!(f, "NoSuchEntity({entity} {details})") write!(f, "EntityDoesNotExist({error})")
} }
Self::AliasedMutability(entity) => write!(f, "AliasedMutability({entity})"), Self::AliasedMutability(entity) => write!(f, "AliasedMutability({entity})"),
} }
@ -88,7 +94,7 @@ impl<'w> PartialEq for QueryEntityError<'w> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {
(Self::QueryDoesNotMatch(e1, _), Self::QueryDoesNotMatch(e2, _)) if e1 == e2 => true, (Self::QueryDoesNotMatch(e1, _), Self::QueryDoesNotMatch(e2, _)) if e1 == e2 => true,
(Self::NoSuchEntity(e1, _), Self::NoSuchEntity(e2, _)) if e1 == e2 => true, (Self::EntityDoesNotExist(e1), Self::EntityDoesNotExist(e2)) if e1 == e2 => true,
(Self::AliasedMutability(e1), Self::AliasedMutability(e2)) if e1 == e2 => true, (Self::AliasedMutability(e1), Self::AliasedMutability(e2)) if e1 == e2 => true,
_ => false, _ => false,
} }

View File

@ -949,7 +949,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// ///
/// let wrong_entity = Entity::from_raw(365); /// let wrong_entity = Entity::from_raw(365);
/// ///
/// assert_eq!(match query_state.get_many(&mut world, [wrong_entity]).unwrap_err() {QueryEntityError::NoSuchEntity(entity, _) => entity, _ => panic!()}, wrong_entity); /// assert_eq!(match query_state.get_many(&mut world, [wrong_entity]).unwrap_err() {QueryEntityError::EntityDoesNotExist(error) => error.entity, _ => panic!()}, wrong_entity);
/// ``` /// ```
#[inline] #[inline]
pub fn get_many<'w, const N: usize>( pub fn get_many<'w, const N: usize>(
@ -1006,7 +1006,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// let wrong_entity = Entity::from_raw(57); /// let wrong_entity = Entity::from_raw(57);
/// let invalid_entity = world.spawn_empty().id(); /// let invalid_entity = world.spawn_empty().id();
/// ///
/// assert_eq!(match query_state.get_many(&mut world, [wrong_entity]).unwrap_err() {QueryEntityError::NoSuchEntity(entity, _) => entity, _ => panic!()}, wrong_entity); /// assert_eq!(match query_state.get_many(&mut world, [wrong_entity]).unwrap_err() {QueryEntityError::EntityDoesNotExist(error) => error.entity, _ => panic!()}, wrong_entity);
/// assert_eq!(match query_state.get_many_mut(&mut world, [invalid_entity]).unwrap_err() {QueryEntityError::QueryDoesNotMatch(entity, _) => entity, _ => panic!()}, invalid_entity); /// assert_eq!(match query_state.get_many_mut(&mut world, [invalid_entity]).unwrap_err() {QueryEntityError::QueryDoesNotMatch(entity, _) => entity, _ => panic!()}, invalid_entity);
/// assert_eq!(query_state.get_many_mut(&mut world, [entities[0], entities[0]]).unwrap_err(), QueryEntityError::AliasedMutability(entities[0])); /// assert_eq!(query_state.get_many_mut(&mut world, [entities[0], entities[0]]).unwrap_err(), QueryEntityError::AliasedMutability(entities[0]));
/// ``` /// ```
@ -1335,7 +1335,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// # let wrong_entity = Entity::from_raw(57); /// # let wrong_entity = Entity::from_raw(57);
/// # let invalid_entity = world.spawn_empty().id(); /// # let invalid_entity = world.spawn_empty().id();
/// ///
/// # assert_eq!(match query_state.get_many(&mut world, [wrong_entity]).unwrap_err() {QueryEntityError::NoSuchEntity(entity, _) => entity, _ => panic!()}, wrong_entity); /// # assert_eq!(match query_state.get_many(&mut world, [wrong_entity]).unwrap_err() {QueryEntityError::EntityDoesNotExist(error) => error.entity, _ => panic!()}, wrong_entity);
/// assert_eq!(match query_state.get_many_mut(&mut world, [invalid_entity]).unwrap_err() {QueryEntityError::QueryDoesNotMatch(entity, _) => entity, _ => panic!()}, invalid_entity); /// assert_eq!(match query_state.get_many_mut(&mut world, [invalid_entity]).unwrap_err() {QueryEntityError::QueryDoesNotMatch(entity, _) => entity, _ => panic!()}, invalid_entity);
/// # assert_eq!(query_state.get_many_mut(&mut world, [entities[0], entities[0]]).unwrap_err(), QueryEntityError::AliasedMutability(entities[0])); /// # assert_eq!(query_state.get_many_mut(&mut world, [entities[0], entities[0]]).unwrap_err(), QueryEntityError::AliasedMutability(entities[0]));
/// ``` /// ```

View File

@ -186,7 +186,7 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
let relationship_target = world.get_entity(entity).unwrap().get::<Self>().unwrap(); let relationship_target = world.get_entity(entity).unwrap().get::<Self>().unwrap();
let mut commands = world.get_raw_command_queue(); let mut commands = world.get_raw_command_queue();
for source_entity in relationship_target.iter() { for source_entity in relationship_target.iter() {
if world.get_entity(source_entity).is_some() { if world.get_entity(source_entity).is_ok() {
commands.push( commands.push(
entity_command::remove::<Self::Relationship>() entity_command::remove::<Self::Relationship>()
.with_entity(source_entity) .with_entity(source_entity)
@ -217,7 +217,7 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
let relationship_target = world.get_entity(entity).unwrap().get::<Self>().unwrap(); let relationship_target = world.get_entity(entity).unwrap().get::<Self>().unwrap();
let mut commands = world.get_raw_command_queue(); let mut commands = world.get_raw_command_queue();
for source_entity in relationship_target.iter() { for source_entity in relationship_target.iter() {
if world.get_entity(source_entity).is_some() { if world.get_entity(source_entity).is_ok() {
commands.push( commands.push(
entity_command::despawn() entity_command::despawn()
.with_entity(source_entity) .with_entity(source_entity)

View File

@ -15,7 +15,7 @@ use crate::{
event::Event, event::Event,
result::Result, result::Result,
system::{command::HandleError, Command, IntoObserverSystem}, system::{command::HandleError, Command, IntoObserverSystem},
world::{error::EntityFetchError, EntityWorldMut, FromWorld, World}, world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld, World},
}; };
use bevy_ptr::OwningPtr; use bevy_ptr::OwningPtr;
@ -96,13 +96,13 @@ pub trait CommandWithEntity<Out> {
fn with_entity(self, entity: Entity) -> impl Command<Out> + HandleError<Out>; fn with_entity(self, entity: Entity) -> impl Command<Out> + HandleError<Out>;
} }
impl<C: EntityCommand> CommandWithEntity<Result<(), EntityFetchError>> for C { impl<C: EntityCommand> CommandWithEntity<Result<(), EntityMutableFetchError>> for C {
fn with_entity( fn with_entity(
self, self,
entity: Entity, entity: Entity,
) -> impl Command<Result<(), EntityFetchError>> + HandleError<Result<(), EntityFetchError>> ) -> impl Command<Result<(), EntityMutableFetchError>>
{ + HandleError<Result<(), EntityMutableFetchError>> {
move |world: &mut World| -> Result<(), EntityFetchError> { move |world: &mut World| -> Result<(), EntityMutableFetchError> {
let entity = world.get_entity_mut(entity)?; let entity = world.get_entity_mut(entity)?;
self.apply(entity); self.apply(entity);
Ok(()) Ok(())
@ -134,7 +134,7 @@ impl<
pub enum EntityCommandError<E> { pub enum EntityCommandError<E> {
/// The entity this [`EntityCommand`] tried to run on could not be fetched. /// The entity this [`EntityCommand`] tried to run on could not be fetched.
#[error(transparent)] #[error(transparent)]
EntityFetchError(#[from] EntityFetchError), EntityFetchError(#[from] EntityMutableFetchError),
/// An error that occurred while running the [`EntityCommand`]. /// An error that occurred while running the [`EntityCommand`].
#[error("{0}")] #[error("{0}")]
CommandFailed(E), CommandFailed(E),
@ -300,6 +300,7 @@ pub fn log_components() -> impl EntityCommand {
let debug_infos: Vec<_> = entity let debug_infos: Vec<_> = entity
.world() .world()
.inspect_entity(entity.id()) .inspect_entity(entity.id())
.expect("Entity existence is verified before an EntityCommand is executed")
.map(ComponentInfo::name) .map(ComponentInfo::name)
.collect(); .collect();
info!("Entity {}: {debug_infos:?}", entity.id()); info!("Entity {}: {debug_infos:?}", entity.id());

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
batching::BatchingStrategy, batching::BatchingStrategy,
component::Tick, component::Tick,
entity::{Entity, EntityBorrow, EntitySet}, entity::{Entity, EntityBorrow, EntityDoesNotExistError, EntitySet},
query::{ query::{
DebugCheckedUnwrap, NopWorldQuery, QueryCombinationIter, QueryData, QueryEntityError, DebugCheckedUnwrap, NopWorldQuery, QueryCombinationIter, QueryData, QueryEntityError,
QueryFilter, QueryIter, QueryManyIter, QueryManyUniqueIter, QueryParIter, QueryParManyIter, QueryFilter, QueryIter, QueryManyIter, QueryManyUniqueIter, QueryParIter, QueryParManyIter,
@ -1310,7 +1310,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// ///
/// assert_eq!( /// assert_eq!(
/// match query.get_many([wrong_entity]).unwrap_err() { /// match query.get_many([wrong_entity]).unwrap_err() {
/// QueryEntityError::NoSuchEntity(entity, _) => entity, /// QueryEntityError::EntityDoesNotExist(error) => error.entity,
/// _ => panic!(), /// _ => panic!(),
/// }, /// },
/// wrong_entity /// wrong_entity
@ -1430,16 +1430,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
// SAFETY: system runs without conflicts with other systems. // SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict // same-system queries have runtime borrow checks when they conflict
unsafe { unsafe {
let location = let location = self
self.world .world
.entities() .entities()
.get(entity) .get(entity)
.ok_or(QueryEntityError::NoSuchEntity( .ok_or(EntityDoesNotExistError::new(entity, self.world.entities()))?;
entity,
self.world
.entities()
.entity_does_not_exist_error_details(entity),
))?;
if !self if !self
.state .state
.matched_archetypes .matched_archetypes
@ -1524,7 +1519,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// .get_many_mut([wrong_entity]) /// .get_many_mut([wrong_entity])
/// .unwrap_err() /// .unwrap_err()
/// { /// {
/// QueryEntityError::NoSuchEntity(entity, _) => entity, /// QueryEntityError::EntityDoesNotExist(error) => error.entity,
/// _ => panic!(), /// _ => panic!(),
/// }, /// },
/// wrong_entity /// wrong_entity

View File

@ -12,7 +12,7 @@ use crate::{
resource::Resource, resource::Resource,
system::{Commands, Query}, system::{Commands, Query},
traversal::Traversal, traversal::Traversal,
world::{error::EntityFetchError, WorldEntityFetch}, world::{error::EntityMutableFetchError, WorldEntityFetch},
}; };
use super::{unsafe_world_cell::UnsafeWorldCell, Mut, World, ON_INSERT, ON_REPLACE}; use super::{unsafe_world_cell::UnsafeWorldCell, Mut, World, ON_INSERT, ON_REPLACE};
@ -94,24 +94,13 @@ impl<'w> DeferredWorld<'w> {
&mut self, &mut self,
entity: Entity, entity: Entity,
f: impl FnOnce(&mut T) -> R, f: impl FnOnce(&mut T) -> R,
) -> Result<Option<R>, EntityFetchError> { ) -> Result<Option<R>, EntityMutableFetchError> {
// If the component is not registered, then it doesn't exist on this entity, so no action required. // If the component is not registered, then it doesn't exist on this entity, so no action required.
let Some(component_id) = self.component_id::<T>() else { let Some(component_id) = self.component_id::<T>() else {
return Ok(None); return Ok(None);
}; };
let entity_cell = match self.get_entity_mut(entity) { let entity_cell = self.get_entity_mut(entity)?;
Ok(cell) => cell,
Err(EntityFetchError::AliasedMutability(..)) => {
return Err(EntityFetchError::AliasedMutability(entity))
}
Err(EntityFetchError::NoSuchEntity(..)) => {
return Err(EntityFetchError::NoSuchEntity(
entity,
self.entities().entity_does_not_exist_error_details(entity),
))
}
};
if !entity_cell.contains::<T>() { if !entity_cell.contains::<T>() {
return Ok(None); return Ok(None);
@ -201,9 +190,9 @@ impl<'w> DeferredWorld<'w> {
/// ///
/// # Errors /// # Errors
/// ///
/// - Returns [`EntityFetchError::NoSuchEntity`] if any of the given `entities` do not exist in the world. /// - Returns [`EntityMutableFetchError::EntityDoesNotExist`] if any of the given `entities` do not exist in the world.
/// - Only the first entity found to be missing will be returned. /// - Only the first entity found to be missing will be returned.
/// - Returns [`EntityFetchError::AliasedMutability`] if the same entity is requested multiple times. /// - Returns [`EntityMutableFetchError::AliasedMutability`] if the same entity is requested multiple times.
/// ///
/// # Examples /// # Examples
/// ///
@ -217,7 +206,7 @@ impl<'w> DeferredWorld<'w> {
pub fn get_entity_mut<F: WorldEntityFetch>( pub fn get_entity_mut<F: WorldEntityFetch>(
&mut self, &mut self,
entities: F, entities: F,
) -> Result<F::DeferredMut<'_>, EntityFetchError> { ) -> Result<F::DeferredMut<'_>, EntityMutableFetchError> {
let cell = self.as_unsafe_world_cell(); let cell = self.as_unsafe_world_cell();
// SAFETY: `&mut self` gives mutable access to the entire world, // SAFETY: `&mut self` gives mutable access to the entire world,
// and prevents any other access to the world. // and prevents any other access to the world.

View File

@ -2,9 +2,9 @@ use alloc::vec::Vec;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use crate::{ use crate::{
entity::{hash_map::EntityHashMap, hash_set::EntityHashSet, Entity}, entity::{hash_map::EntityHashMap, hash_set::EntityHashSet, Entity, EntityDoesNotExistError},
world::{ world::{
error::EntityFetchError, unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityRef, error::EntityMutableFetchError, unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityRef,
EntityWorldMut, EntityWorldMut,
}, },
}; };
@ -56,8 +56,11 @@ pub unsafe trait WorldEntityFetch {
/// ///
/// # Errors /// # Errors
/// ///
/// - Returns [`Entity`] if the entity does not exist. /// - Returns [`EntityDoesNotExistError`] if the entity does not exist.
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity>; unsafe fn fetch_ref(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Ref<'_>, EntityDoesNotExistError>;
/// Returns mutable reference(s) to the entities with the given [`Entity`] /// Returns mutable reference(s) to the entities with the given [`Entity`]
/// IDs, as determined by `self`. /// IDs, as determined by `self`.
@ -70,11 +73,13 @@ pub unsafe trait WorldEntityFetch {
/// ///
/// # Errors /// # Errors
/// ///
/// - Returns [`EntityFetchError::NoSuchEntity`] if the entity does not exist. /// - Returns [`EntityMutableFetchError::EntityDoesNotExist`] if the entity does not exist.
/// - Returns [`EntityFetchError::AliasedMutability`] if the entity was /// - Returns [`EntityMutableFetchError::AliasedMutability`] if the entity was
/// requested mutably more than once. /// requested mutably more than once.
unsafe fn fetch_mut(self, cell: UnsafeWorldCell<'_>) unsafe fn fetch_mut(
-> Result<Self::Mut<'_>, EntityFetchError>; self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityMutableFetchError>;
/// Returns mutable reference(s) to the entities with the given [`Entity`] /// Returns mutable reference(s) to the entities with the given [`Entity`]
/// IDs, as determined by `self`, but without structural mutability. /// IDs, as determined by `self`, but without structural mutability.
@ -91,13 +96,13 @@ pub unsafe trait WorldEntityFetch {
/// ///
/// # Errors /// # Errors
/// ///
/// - Returns [`EntityFetchError::NoSuchEntity`] if the entity does not exist. /// - Returns [`EntityMutableFetchError::EntityDoesNotExist`] if the entity does not exist.
/// - Returns [`EntityFetchError::AliasedMutability`] if the entity was /// - Returns [`EntityMutableFetchError::AliasedMutability`] if the entity was
/// requested mutably more than once. /// requested mutably more than once.
unsafe fn fetch_deferred_mut( unsafe fn fetch_deferred_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError>; ) -> Result<Self::DeferredMut<'_>, EntityMutableFetchError>;
} }
// SAFETY: // SAFETY:
@ -109,8 +114,11 @@ unsafe impl WorldEntityFetch for Entity {
type Mut<'w> = EntityWorldMut<'w>; type Mut<'w> = EntityWorldMut<'w>;
type DeferredMut<'w> = EntityMut<'w>; type DeferredMut<'w> = EntityMut<'w>;
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> { unsafe fn fetch_ref(
let ecell = cell.get_entity(self).ok_or(self)?; self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Ref<'_>, EntityDoesNotExistError> {
let ecell = cell.get_entity(self)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity. // SAFETY: caller ensures that the world cell has read-only access to the entity.
Ok(unsafe { EntityRef::new(ecell) }) Ok(unsafe { EntityRef::new(ecell) })
} }
@ -118,14 +126,11 @@ unsafe impl WorldEntityFetch for Entity {
unsafe fn fetch_mut( unsafe fn fetch_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> { ) -> Result<Self::Mut<'_>, EntityMutableFetchError> {
let location = cell let location = cell
.entities() .entities()
.get(self) .get(self)
.ok_or(EntityFetchError::NoSuchEntity( .ok_or(EntityDoesNotExistError::new(self, cell.entities()))?;
self,
cell.entities().entity_does_not_exist_error_details(self),
))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity. // SAFETY: caller ensures that the world cell has mutable access to the entity.
let world = unsafe { cell.world_mut() }; let world = unsafe { cell.world_mut() };
// SAFETY: location was fetched from the same world's `Entities`. // SAFETY: location was fetched from the same world's `Entities`.
@ -135,11 +140,8 @@ unsafe impl WorldEntityFetch for Entity {
unsafe fn fetch_deferred_mut( unsafe fn fetch_deferred_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> { ) -> Result<Self::DeferredMut<'_>, EntityMutableFetchError> {
let ecell = cell.get_entity(self).ok_or(EntityFetchError::NoSuchEntity( let ecell = cell.get_entity(self)?;
self,
cell.entities().entity_does_not_exist_error_details(self),
))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity. // SAFETY: caller ensures that the world cell has mutable access to the entity.
Ok(unsafe { EntityMut::new(ecell) }) Ok(unsafe { EntityMut::new(ecell) })
} }
@ -154,21 +156,24 @@ unsafe impl<const N: usize> WorldEntityFetch for [Entity; N] {
type Mut<'w> = [EntityMut<'w>; N]; type Mut<'w> = [EntityMut<'w>; N];
type DeferredMut<'w> = [EntityMut<'w>; N]; type DeferredMut<'w> = [EntityMut<'w>; N];
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> { unsafe fn fetch_ref(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Ref<'_>, EntityDoesNotExistError> {
<&Self>::fetch_ref(&self, cell) <&Self>::fetch_ref(&self, cell)
} }
unsafe fn fetch_mut( unsafe fn fetch_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> { ) -> Result<Self::Mut<'_>, EntityMutableFetchError> {
<&Self>::fetch_mut(&self, cell) <&Self>::fetch_mut(&self, cell)
} }
unsafe fn fetch_deferred_mut( unsafe fn fetch_deferred_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> { ) -> Result<Self::DeferredMut<'_>, EntityMutableFetchError> {
<&Self>::fetch_deferred_mut(&self, cell) <&Self>::fetch_deferred_mut(&self, cell)
} }
} }
@ -182,10 +187,13 @@ unsafe impl<const N: usize> WorldEntityFetch for &'_ [Entity; N] {
type Mut<'w> = [EntityMut<'w>; N]; type Mut<'w> = [EntityMut<'w>; N];
type DeferredMut<'w> = [EntityMut<'w>; N]; type DeferredMut<'w> = [EntityMut<'w>; N];
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> { unsafe fn fetch_ref(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Ref<'_>, EntityDoesNotExistError> {
let mut refs = [MaybeUninit::uninit(); N]; let mut refs = [MaybeUninit::uninit(); N];
for (r, &id) in core::iter::zip(&mut refs, self) { for (r, &id) in core::iter::zip(&mut refs, self) {
let ecell = cell.get_entity(id).ok_or(id)?; let ecell = cell.get_entity(id)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity. // SAFETY: caller ensures that the world cell has read-only access to the entity.
*r = MaybeUninit::new(unsafe { EntityRef::new(ecell) }); *r = MaybeUninit::new(unsafe { EntityRef::new(ecell) });
} }
@ -199,22 +207,19 @@ unsafe impl<const N: usize> WorldEntityFetch for &'_ [Entity; N] {
unsafe fn fetch_mut( unsafe fn fetch_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> { ) -> Result<Self::Mut<'_>, EntityMutableFetchError> {
// Check for duplicate entities. // Check for duplicate entities.
for i in 0..self.len() { for i in 0..self.len() {
for j in 0..i { for j in 0..i {
if self[i] == self[j] { if self[i] == self[j] {
return Err(EntityFetchError::AliasedMutability(self[i])); return Err(EntityMutableFetchError::AliasedMutability(self[i]));
} }
} }
} }
let mut refs = [const { MaybeUninit::uninit() }; N]; let mut refs = [const { MaybeUninit::uninit() }; N];
for (r, &id) in core::iter::zip(&mut refs, self) { for (r, &id) in core::iter::zip(&mut refs, self) {
let ecell = cell.get_entity(id).ok_or(EntityFetchError::NoSuchEntity( let ecell = cell.get_entity(id)?;
id,
cell.entities().entity_does_not_exist_error_details(id),
))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity. // SAFETY: caller ensures that the world cell has mutable access to the entity.
*r = MaybeUninit::new(unsafe { EntityMut::new(ecell) }); *r = MaybeUninit::new(unsafe { EntityMut::new(ecell) });
} }
@ -228,7 +233,7 @@ unsafe impl<const N: usize> WorldEntityFetch for &'_ [Entity; N] {
unsafe fn fetch_deferred_mut( unsafe fn fetch_deferred_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> { ) -> Result<Self::DeferredMut<'_>, EntityMutableFetchError> {
// SAFETY: caller ensures that the world cell has mutable access to the entity, // SAFETY: caller ensures that the world cell has mutable access to the entity,
// and `fetch_mut` does not return structurally-mutable references. // and `fetch_mut` does not return structurally-mutable references.
unsafe { self.fetch_mut(cell) } unsafe { self.fetch_mut(cell) }
@ -244,10 +249,13 @@ unsafe impl WorldEntityFetch for &'_ [Entity] {
type Mut<'w> = Vec<EntityMut<'w>>; type Mut<'w> = Vec<EntityMut<'w>>;
type DeferredMut<'w> = Vec<EntityMut<'w>>; type DeferredMut<'w> = Vec<EntityMut<'w>>;
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> { unsafe fn fetch_ref(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Ref<'_>, EntityDoesNotExistError> {
let mut refs = Vec::with_capacity(self.len()); let mut refs = Vec::with_capacity(self.len());
for &id in self { for &id in self {
let ecell = cell.get_entity(id).ok_or(id)?; let ecell = cell.get_entity(id)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity. // SAFETY: caller ensures that the world cell has read-only access to the entity.
refs.push(unsafe { EntityRef::new(ecell) }); refs.push(unsafe { EntityRef::new(ecell) });
} }
@ -258,22 +266,19 @@ unsafe impl WorldEntityFetch for &'_ [Entity] {
unsafe fn fetch_mut( unsafe fn fetch_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> { ) -> Result<Self::Mut<'_>, EntityMutableFetchError> {
// Check for duplicate entities. // Check for duplicate entities.
for i in 0..self.len() { for i in 0..self.len() {
for j in 0..i { for j in 0..i {
if self[i] == self[j] { if self[i] == self[j] {
return Err(EntityFetchError::AliasedMutability(self[i])); return Err(EntityMutableFetchError::AliasedMutability(self[i]));
} }
} }
} }
let mut refs = Vec::with_capacity(self.len()); let mut refs = Vec::with_capacity(self.len());
for &id in self { for &id in self {
let ecell = cell.get_entity(id).ok_or(EntityFetchError::NoSuchEntity( let ecell = cell.get_entity(id)?;
id,
cell.entities().entity_does_not_exist_error_details(id),
))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity. // SAFETY: caller ensures that the world cell has mutable access to the entity.
refs.push(unsafe { EntityMut::new(ecell) }); refs.push(unsafe { EntityMut::new(ecell) });
} }
@ -284,7 +289,7 @@ unsafe impl WorldEntityFetch for &'_ [Entity] {
unsafe fn fetch_deferred_mut( unsafe fn fetch_deferred_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> { ) -> Result<Self::DeferredMut<'_>, EntityMutableFetchError> {
// SAFETY: caller ensures that the world cell has mutable access to the entity, // SAFETY: caller ensures that the world cell has mutable access to the entity,
// and `fetch_mut` does not return structurally-mutable references. // and `fetch_mut` does not return structurally-mutable references.
unsafe { self.fetch_mut(cell) } unsafe { self.fetch_mut(cell) }
@ -300,10 +305,13 @@ unsafe impl WorldEntityFetch for &'_ EntityHashSet {
type Mut<'w> = EntityHashMap<EntityMut<'w>>; type Mut<'w> = EntityHashMap<EntityMut<'w>>;
type DeferredMut<'w> = EntityHashMap<EntityMut<'w>>; type DeferredMut<'w> = EntityHashMap<EntityMut<'w>>;
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> { unsafe fn fetch_ref(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Ref<'_>, EntityDoesNotExistError> {
let mut refs = EntityHashMap::with_capacity(self.len()); let mut refs = EntityHashMap::with_capacity(self.len());
for &id in self { for &id in self {
let ecell = cell.get_entity(id).ok_or(id)?; let ecell = cell.get_entity(id)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity. // SAFETY: caller ensures that the world cell has read-only access to the entity.
refs.insert(id, unsafe { EntityRef::new(ecell) }); refs.insert(id, unsafe { EntityRef::new(ecell) });
} }
@ -313,13 +321,10 @@ unsafe impl WorldEntityFetch for &'_ EntityHashSet {
unsafe fn fetch_mut( unsafe fn fetch_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> { ) -> Result<Self::Mut<'_>, EntityMutableFetchError> {
let mut refs = EntityHashMap::with_capacity(self.len()); let mut refs = EntityHashMap::with_capacity(self.len());
for &id in self { for &id in self {
let ecell = cell.get_entity(id).ok_or(EntityFetchError::NoSuchEntity( let ecell = cell.get_entity(id)?;
id,
cell.entities().entity_does_not_exist_error_details(id),
))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity. // SAFETY: caller ensures that the world cell has mutable access to the entity.
refs.insert(id, unsafe { EntityMut::new(ecell) }); refs.insert(id, unsafe { EntityMut::new(ecell) });
} }
@ -329,7 +334,7 @@ unsafe impl WorldEntityFetch for &'_ EntityHashSet {
unsafe fn fetch_deferred_mut( unsafe fn fetch_deferred_mut(
self, self,
cell: UnsafeWorldCell<'_>, cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> { ) -> Result<Self::DeferredMut<'_>, EntityMutableFetchError> {
// SAFETY: caller ensures that the world cell has mutable access to the entity, // SAFETY: caller ensures that the world cell has mutable access to the entity,
// and `fetch_mut` does not return structurally-mutable references. // and `fetch_mut` does not return structurally-mutable references.
unsafe { self.fetch_mut(cell) } unsafe { self.fetch_mut(cell) }

View File

@ -1,39 +1,26 @@
//! Contains error types returned by bevy's schedule. //! Contains error types returned by bevy's schedule.
use alloc::vec::Vec; use alloc::vec::Vec;
use thiserror::Error;
use crate::{ use crate::{
component::ComponentId, component::ComponentId,
entity::{Entity, EntityDoesNotExistDetails}, entity::{Entity, EntityDoesNotExistError},
schedule::InternedScheduleLabel, schedule::InternedScheduleLabel,
}; };
/// The error type returned by [`World::try_run_schedule`] if the provided schedule does not exist. /// The error type returned by [`World::try_run_schedule`] if the provided schedule does not exist.
/// ///
/// [`World::try_run_schedule`]: crate::world::World::try_run_schedule /// [`World::try_run_schedule`]: crate::world::World::try_run_schedule
#[derive(Error, Debug)] #[derive(thiserror::Error, Debug)]
#[error("The schedule with the label {0:?} was not found.")] #[error("The schedule with the label {0:?} was not found.")]
pub struct TryRunScheduleError(pub InternedScheduleLabel); pub struct TryRunScheduleError(pub InternedScheduleLabel);
/// The error type returned by [`World::try_despawn`] if the provided entity does not exist.
///
/// [`World::try_despawn`]: crate::world::World::try_despawn
#[derive(Error, Debug, Clone, Copy)]
#[error("Could not despawn the entity with ID {entity} because it {details}")]
pub struct TryDespawnError {
/// The entity's ID.
pub entity: Entity,
/// Details on why the entity does not exist, if available.
pub details: EntityDoesNotExistDetails,
}
/// The error type returned by [`World::try_insert_batch`] and [`World::try_insert_batch_if_new`] /// The error type returned by [`World::try_insert_batch`] and [`World::try_insert_batch_if_new`]
/// if any of the provided entities do not exist. /// if any of the provided entities do not exist.
/// ///
/// [`World::try_insert_batch`]: crate::world::World::try_insert_batch /// [`World::try_insert_batch`]: crate::world::World::try_insert_batch
/// [`World::try_insert_batch_if_new`]: crate::world::World::try_insert_batch_if_new /// [`World::try_insert_batch_if_new`]: crate::world::World::try_insert_batch_if_new
#[derive(Error, Debug, Clone)] #[derive(thiserror::Error, Debug, Clone)]
#[error("Could not insert bundles of type {bundle_type} into the entities with the following IDs because they do not exist: {entities:?}")] #[error("Could not insert bundles of type {bundle_type} into the entities with the following IDs because they do not exist: {entities:?}")]
pub struct TryInsertBatchError { pub struct TryInsertBatchError {
/// The bundles' type name. /// The bundles' type name.
@ -42,8 +29,13 @@ pub struct TryInsertBatchError {
pub entities: Vec<Entity>, pub entities: Vec<Entity>,
} }
/// An error that occurs when a specified [`Entity`] could not be despawned.
#[derive(thiserror::Error, Debug, Clone, Copy)]
#[error("Could not despawn entity: {0}")]
pub struct EntityDespawnError(#[from] pub EntityMutableFetchError);
/// An error that occurs when dynamically retrieving components from an entity. /// An error that occurs when dynamically retrieving components from an entity.
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)] #[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityComponentError { pub enum EntityComponentError {
/// The component with the given [`ComponentId`] does not exist on the entity. /// The component with the given [`ComponentId`] does not exist on the entity.
#[error("The component with ID {0:?} does not exist on the entity.")] #[error("The component with ID {0:?} does not exist on the entity.")]
@ -54,24 +46,12 @@ pub enum EntityComponentError {
} }
/// An error that occurs when fetching entities mutably from a world. /// An error that occurs when fetching entities mutably from a world.
#[derive(Error, Debug, Clone, Copy)] #[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityFetchError { pub enum EntityMutableFetchError {
/// The entity with the given ID does not exist. /// The entity with the given ID does not exist.
#[error("The entity with ID {0} {1}")] #[error(transparent)]
NoSuchEntity(Entity, EntityDoesNotExistDetails), EntityDoesNotExist(#[from] EntityDoesNotExistError),
/// The entity with the given ID was requested mutably more than once. /// The entity with the given ID was requested mutably more than once.
#[error("The entity with ID {0} was requested mutably more than once")] #[error("The entity with ID {0} was requested mutably more than once")]
AliasedMutability(Entity), AliasedMutability(Entity),
} }
impl PartialEq for EntityFetchError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::NoSuchEntity(e1, _), Self::NoSuchEntity(e2, _)) if e1 == e2 => true,
(Self::AliasedMutability(e1), Self::AliasedMutability(e2)) if e1 == e2 => true,
_ => false,
}
}
}
impl Eq for EntityFetchError {}

View File

@ -41,7 +41,9 @@ use crate::{
Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks, Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks,
Components, Mutable, RequiredComponents, RequiredComponentsError, Tick, Components, Mutable, RequiredComponents, RequiredComponentsError, Tick,
}, },
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, entity::{
AllocAtWithoutReplacement, Entities, Entity, EntityDoesNotExistError, EntityLocation,
},
entity_disabling::DefaultQueryFilters, entity_disabling::DefaultQueryFilters,
event::{Event, EventId, Events, SendBatchIds}, event::{Event, EventId, Events, SendBatchIds},
observer::Observers, observer::Observers,
@ -53,7 +55,9 @@ use crate::{
system::Commands, system::Commands,
world::{ world::{
command_queue::RawCommandQueue, command_queue::RawCommandQueue,
error::{EntityFetchError, TryDespawnError, TryInsertBatchError, TryRunScheduleError}, error::{
EntityDespawnError, EntityMutableFetchError, TryInsertBatchError, TryRunScheduleError,
},
}, },
}; };
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
@ -694,7 +698,7 @@ impl World {
match self.get_entity(entities) { match self.get_entity(entities) {
Ok(fetched) => fetched, Ok(fetched) => fetched,
Err(entity) => panic_no_entity(self, entity), Err(error) => panic_no_entity(self, error.entity),
} }
} }
@ -821,7 +825,7 @@ impl World {
#[inline(never)] #[inline(never)]
#[cold] #[cold]
#[track_caller] #[track_caller]
fn panic_on_err(e: EntityFetchError) -> ! { fn panic_on_err(e: EntityMutableFetchError) -> ! {
panic!("{e}"); panic!("{e}");
} }
@ -833,25 +837,23 @@ impl World {
/// Returns the components of an [`Entity`] through [`ComponentInfo`]. /// Returns the components of an [`Entity`] through [`ComponentInfo`].
#[inline] #[inline]
pub fn inspect_entity(&self, entity: Entity) -> impl Iterator<Item = &ComponentInfo> { pub fn inspect_entity(
&self,
entity: Entity,
) -> Result<impl Iterator<Item = &ComponentInfo>, EntityDoesNotExistError> {
let entity_location = self let entity_location = self
.entities() .entities()
.get(entity) .get(entity)
.unwrap_or_else(|| panic!("Entity {entity} does not exist")); .ok_or(EntityDoesNotExistError::new(entity, self.entities()))?;
let archetype = self let archetype = self
.archetypes() .archetypes()
.get(entity_location.archetype_id) .get(entity_location.archetype_id)
.unwrap_or_else(|| { .expect("ArchetypeId was retrieved from an EntityLocation and should correspond to an Archetype");
panic!(
"Archetype {:?} does not exist",
entity_location.archetype_id
)
});
archetype Ok(archetype
.components() .components()
.filter_map(|id| self.components().get_info(id)) .filter_map(|id| self.components().get_info(id)))
} }
/// Returns [`EntityRef`]s that expose read-only operations for the given /// Returns [`EntityRef`]s that expose read-only operations for the given
@ -869,7 +871,7 @@ impl World {
/// # Errors /// # Errors
/// ///
/// If any of the given `entities` do not exist in the world, the first /// If any of the given `entities` do not exist in the world, the first
/// [`Entity`] found to be missing will be returned in the [`Err`]. /// [`Entity`] found to be missing will return an [`EntityDoesNotExistError`].
/// ///
/// # Examples /// # Examples
/// ///
@ -877,7 +879,10 @@ impl World {
/// ///
/// [`EntityHashSet`]: crate::entity::hash_set::EntityHashSet /// [`EntityHashSet`]: crate::entity::hash_set::EntityHashSet
#[inline] #[inline]
pub fn get_entity<F: WorldEntityFetch>(&self, entities: F) -> Result<F::Ref<'_>, Entity> { pub fn get_entity<F: WorldEntityFetch>(
&self,
entities: F,
) -> Result<F::Ref<'_>, EntityDoesNotExistError> {
let cell = self.as_unsafe_world_cell_readonly(); let cell = self.as_unsafe_world_cell_readonly();
// SAFETY: `&self` gives read access to the entire world, and prevents mutable access. // SAFETY: `&self` gives read access to the entire world, and prevents mutable access.
unsafe { entities.fetch_ref(cell) } unsafe { entities.fetch_ref(cell) }
@ -905,9 +910,9 @@ impl World {
/// ///
/// # Errors /// # Errors
/// ///
/// - Returns [`EntityFetchError::NoSuchEntity`] if any of the given `entities` do not exist in the world. /// - Returns [`EntityMutableFetchError::EntityDoesNotExist`] if any of the given `entities` do not exist in the world.
/// - Only the first entity found to be missing will be returned. /// - Only the first entity found to be missing will be returned.
/// - Returns [`EntityFetchError::AliasedMutability`] if the same entity is requested multiple times. /// - Returns [`EntityMutableFetchError::AliasedMutability`] if the same entity is requested multiple times.
/// ///
/// # Examples /// # Examples
/// ///
@ -918,7 +923,7 @@ impl World {
pub fn get_entity_mut<F: WorldEntityFetch>( pub fn get_entity_mut<F: WorldEntityFetch>(
&mut self, &mut self,
entities: F, entities: F,
) -> Result<F::Mut<'_>, EntityFetchError> { ) -> Result<F::Mut<'_>, EntityMutableFetchError> {
let cell = self.as_unsafe_world_cell(); let cell = self.as_unsafe_world_cell();
// SAFETY: `&mut self` gives mutable access to the entire world, // SAFETY: `&mut self` gives mutable access to the entire world,
// and prevents any other access to the world. // and prevents any other access to the world.
@ -1244,21 +1249,10 @@ impl World {
&mut self, &mut self,
entity: Entity, entity: Entity,
f: impl FnOnce(&mut T) -> R, f: impl FnOnce(&mut T) -> R,
) -> Result<Option<R>, EntityFetchError> { ) -> Result<Option<R>, EntityMutableFetchError> {
let mut world = DeferredWorld::from(&mut *self); let mut world = DeferredWorld::from(&mut *self);
let result = match world.modify_component(entity, f) { let result = world.modify_component(entity, f)?;
Ok(result) => result,
Err(EntityFetchError::AliasedMutability(..)) => {
return Err(EntityFetchError::AliasedMutability(entity))
}
Err(EntityFetchError::NoSuchEntity(..)) => {
return Err(EntityFetchError::NoSuchEntity(
entity,
self.entities().entity_does_not_exist_error_details(entity),
))
}
};
self.flush(); self.flush();
Ok(result) Ok(result)
@ -1304,7 +1298,7 @@ impl World {
/// Despawns the given `entity`, if it exists. This will also remove all of the entity's /// Despawns the given `entity`, if it exists. This will also remove all of the entity's
/// [`Components`](Component). /// [`Components`](Component).
/// ///
/// Returns a [`TryDespawnError`] if the entity does not exist. /// Returns an [`EntityDespawnError`] if the entity does not exist.
/// ///
/// # Note /// # Note
/// ///
@ -1312,7 +1306,7 @@ impl World {
/// to despawn descendants. For example, this will recursively despawn [`Children`](crate::hierarchy::Children). /// to despawn descendants. For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
#[track_caller] #[track_caller]
#[inline] #[inline]
pub fn try_despawn(&mut self, entity: Entity) -> Result<(), TryDespawnError> { pub fn try_despawn(&mut self, entity: Entity) -> Result<(), EntityDespawnError> {
self.despawn_with_caller(entity, MaybeLocation::caller()) self.despawn_with_caller(entity, MaybeLocation::caller())
} }
@ -1321,17 +1315,11 @@ impl World {
&mut self, &mut self,
entity: Entity, entity: Entity,
caller: MaybeLocation, caller: MaybeLocation,
) -> Result<(), TryDespawnError> { ) -> Result<(), EntityDespawnError> {
self.flush(); self.flush();
if let Ok(entity) = self.get_entity_mut(entity) { let entity = self.get_entity_mut(entity)?;
entity.despawn_with_caller(caller); entity.despawn_with_caller(caller);
Ok(()) Ok(())
} else {
Err(TryDespawnError {
entity,
details: self.entities().entity_does_not_exist_error_details(entity),
})
}
} }
/// Clears the internal component tracker state. /// Clears the internal component tracker state.
@ -3607,7 +3595,7 @@ mod tests {
entity_disabling::{DefaultQueryFilters, Disabled}, entity_disabling::{DefaultQueryFilters, Disabled},
ptr::OwningPtr, ptr::OwningPtr,
resource::Resource, resource::Resource,
world::error::EntityFetchError, world::error::EntityMutableFetchError,
}; };
use alloc::{ use alloc::{
borrow::ToOwned, borrow::ToOwned,
@ -3992,39 +3980,39 @@ mod tests {
let bar_id = TypeId::of::<Bar>(); let bar_id = TypeId::of::<Bar>();
let baz_id = TypeId::of::<Baz>(); let baz_id = TypeId::of::<Baz>();
assert_eq!( assert_eq!(
to_type_ids(world.inspect_entity(ent0).collect()), to_type_ids(world.inspect_entity(ent0).unwrap().collect()),
[Some(foo_id), Some(bar_id), Some(baz_id)] [Some(foo_id), Some(bar_id), Some(baz_id)]
.into_iter() .into_iter()
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
); );
assert_eq!( assert_eq!(
to_type_ids(world.inspect_entity(ent1).collect()), to_type_ids(world.inspect_entity(ent1).unwrap().collect()),
[Some(foo_id), Some(bar_id)] [Some(foo_id), Some(bar_id)]
.into_iter() .into_iter()
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
); );
assert_eq!( assert_eq!(
to_type_ids(world.inspect_entity(ent2).collect()), to_type_ids(world.inspect_entity(ent2).unwrap().collect()),
[Some(bar_id), Some(baz_id)] [Some(bar_id), Some(baz_id)]
.into_iter() .into_iter()
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
); );
assert_eq!( assert_eq!(
to_type_ids(world.inspect_entity(ent3).collect()), to_type_ids(world.inspect_entity(ent3).unwrap().collect()),
[Some(foo_id), Some(baz_id)] [Some(foo_id), Some(baz_id)]
.into_iter() .into_iter()
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
); );
assert_eq!( assert_eq!(
to_type_ids(world.inspect_entity(ent4).collect()), to_type_ids(world.inspect_entity(ent4).unwrap().collect()),
[Some(foo_id)].into_iter().collect::<HashSet<_>>() [Some(foo_id)].into_iter().collect::<HashSet<_>>()
); );
assert_eq!( assert_eq!(
to_type_ids(world.inspect_entity(ent5).collect()), to_type_ids(world.inspect_entity(ent5).unwrap().collect()),
[Some(bar_id)].into_iter().collect::<HashSet<_>>() [Some(bar_id)].into_iter().collect::<HashSet<_>>()
); );
assert_eq!( assert_eq!(
to_type_ids(world.inspect_entity(ent6).collect()), to_type_ids(world.inspect_entity(ent6).unwrap().collect()),
[Some(baz_id)].into_iter().collect::<HashSet<_>>() [Some(baz_id)].into_iter().collect::<HashSet<_>>()
); );
} }
@ -4171,20 +4159,34 @@ mod tests {
world.entity_mut(e1).despawn(); world.entity_mut(e1).despawn();
assert_eq!(Err(e1), world.get_entity(e1).map(|_| {})); assert_eq!(
assert_eq!(Err(e1), world.get_entity([e1, e2]).map(|_| {})); Err(e1),
world.get_entity(e1).map(|_| {}).map_err(|e| e.entity)
);
assert_eq!(
Err(e1),
world.get_entity([e1, e2]).map(|_| {}).map_err(|e| e.entity)
);
assert_eq!( assert_eq!(
Err(e1), Err(e1),
world world
.get_entity(&[e1, e2] /* this is an array not a slice */) .get_entity(&[e1, e2] /* this is an array not a slice */)
.map(|_| {}) .map(|_| {})
.map_err(|e| e.entity)
);
assert_eq!(
Err(e1),
world
.get_entity(&vec![e1, e2][..])
.map(|_| {})
.map_err(|e| e.entity)
); );
assert_eq!(Err(e1), world.get_entity(&vec![e1, e2][..]).map(|_| {}));
assert_eq!( assert_eq!(
Err(e1), Err(e1),
world world
.get_entity(&EntityHashSet::from_iter([e1, e2])) .get_entity(&EntityHashSet::from_iter([e1, e2]))
.map(|_| {}) .map(|_| {})
.map_err(|e| e.entity)
); );
} }
@ -4206,17 +4208,17 @@ mod tests {
.is_ok()); .is_ok());
assert_eq!( assert_eq!(
Err(EntityFetchError::AliasedMutability(e1)), Err(EntityMutableFetchError::AliasedMutability(e1)),
world.get_entity_mut([e1, e2, e1]).map(|_| {}) world.get_entity_mut([e1, e2, e1]).map(|_| {})
); );
assert_eq!( assert_eq!(
Err(EntityFetchError::AliasedMutability(e1)), Err(EntityMutableFetchError::AliasedMutability(e1)),
world world
.get_entity_mut(&[e1, e2, e1] /* this is an array not a slice */) .get_entity_mut(&[e1, e2, e1] /* this is an array not a slice */)
.map(|_| {}) .map(|_| {})
); );
assert_eq!( assert_eq!(
Err(EntityFetchError::AliasedMutability(e1)), Err(EntityMutableFetchError::AliasedMutability(e1)),
world.get_entity_mut(&vec![e1, e2, e1][..]).map(|_| {}) world.get_entity_mut(&vec![e1, e2, e1][..]).map(|_| {})
); );
// Aliased mutability isn't allowed by HashSets // Aliased mutability isn't allowed by HashSets
@ -4228,25 +4230,25 @@ mod tests {
assert!(matches!( assert!(matches!(
world.get_entity_mut(e1).map(|_| {}), world.get_entity_mut(e1).map(|_| {}),
Err(EntityFetchError::NoSuchEntity(e, ..)) if e == e1 Err(EntityMutableFetchError::EntityDoesNotExist(e)) if e.entity == e1
)); ));
assert!(matches!( assert!(matches!(
world.get_entity_mut([e1, e2]).map(|_| {}), world.get_entity_mut([e1, e2]).map(|_| {}),
Err(EntityFetchError::NoSuchEntity(e,..)) if e == e1)); Err(EntityMutableFetchError::EntityDoesNotExist(e)) if e.entity == e1));
assert!(matches!( assert!(matches!(
world world
.get_entity_mut(&[e1, e2] /* this is an array not a slice */) .get_entity_mut(&[e1, e2] /* this is an array not a slice */)
.map(|_| {}), .map(|_| {}),
Err(EntityFetchError::NoSuchEntity(e, ..)) if e == e1)); Err(EntityMutableFetchError::EntityDoesNotExist(e)) if e.entity == e1));
assert!(matches!( assert!(matches!(
world.get_entity_mut(&vec![e1, e2][..]).map(|_| {}), world.get_entity_mut(&vec![e1, e2][..]).map(|_| {}),
Err(EntityFetchError::NoSuchEntity(e, ..)) if e == e1, Err(EntityMutableFetchError::EntityDoesNotExist(e)) if e.entity == e1,
)); ));
assert!(matches!( assert!(matches!(
world world
.get_entity_mut(&EntityHashSet::from_iter([e1, e2])) .get_entity_mut(&EntityHashSet::from_iter([e1, e2]))
.map(|_| {}), .map(|_| {}),
Err(EntityFetchError::NoSuchEntity(e, ..)) if e == e1)); Err(EntityMutableFetchError::EntityDoesNotExist(e)) if e.entity == e1));
} }
#[test] #[test]

View File

@ -6,7 +6,7 @@ use crate::{
bundle::Bundles, bundle::Bundles,
change_detection::{MaybeLocation, MutUntyped, Ticks, TicksMut}, change_detection::{MaybeLocation, MutUntyped, Ticks, TicksMut},
component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells}, component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells},
entity::{Entities, Entity, EntityBorrow, EntityLocation}, entity::{Entities, Entity, EntityBorrow, EntityDoesNotExistError, EntityLocation},
observer::Observers, observer::Observers,
prelude::Component, prelude::Component,
query::{DebugCheckedUnwrap, ReadOnlyQueryData}, query::{DebugCheckedUnwrap, ReadOnlyQueryData},
@ -351,9 +351,15 @@ impl<'w> UnsafeWorldCell<'w> {
/// Retrieves an [`UnsafeEntityCell`] that exposes read and write operations for the given `entity`. /// Retrieves an [`UnsafeEntityCell`] that exposes read and write operations for the given `entity`.
/// Similar to the [`UnsafeWorldCell`], you are in charge of making sure that no aliasing rules are violated. /// Similar to the [`UnsafeWorldCell`], you are in charge of making sure that no aliasing rules are violated.
#[inline] #[inline]
pub fn get_entity(self, entity: Entity) -> Option<UnsafeEntityCell<'w>> { pub fn get_entity(
let location = self.entities().get(entity)?; self,
Some(UnsafeEntityCell::new(self, entity, location)) entity: Entity,
) -> Result<UnsafeEntityCell<'w>, EntityDoesNotExistError> {
let location = self
.entities()
.get(entity)
.ok_or(EntityDoesNotExistError::new(entity, self.entities()))?;
Ok(UnsafeEntityCell::new(self, entity, location))
} }
/// Gets a reference to the resource of the given type if it exists /// Gets a reference to the resource of the given type if it exists

View File

@ -332,7 +332,9 @@ where
let view = match self.view.get_manual(world, view) { let view = match self.view.get_manual(world, view) {
Ok(view) => view, Ok(view) => view,
Err(err) => match err { Err(err) => match err {
QueryEntityError::NoSuchEntity(_, _) => return Err(DrawError::ViewEntityNotFound), QueryEntityError::EntityDoesNotExist(_) => {
return Err(DrawError::ViewEntityNotFound)
}
QueryEntityError::QueryDoesNotMatch(_, _) QueryEntityError::QueryDoesNotMatch(_, _)
| QueryEntityError::AliasedMutability(_) => { | QueryEntityError::AliasedMutability(_) => {
return Err(DrawError::InvalidViewQuery) return Err(DrawError::InvalidViewQuery)

View File

@ -52,11 +52,11 @@ fn map_error(err: QueryEntityError, ancestor: bool) -> ComputeGlobalTransformErr
use ComputeGlobalTransformError::*; use ComputeGlobalTransformError::*;
match err { match err {
QueryEntityError::QueryDoesNotMatch(entity, _) => MissingTransform(entity), QueryEntityError::QueryDoesNotMatch(entity, _) => MissingTransform(entity),
QueryEntityError::NoSuchEntity(entity, _) => { QueryEntityError::EntityDoesNotExist(error) => {
if ancestor { if ancestor {
MalformedHierarchy(entity) MalformedHierarchy(error.entity)
} else { } else {
NoSuchEntity(entity) NoSuchEntity(error.entity)
} }
} }
QueryEntityError::AliasedMutability(_) => unreachable!(), QueryEntityError::AliasedMutability(_) => unreachable!(),