Skip to main content

unlit_ecs/
error.rs

1//! The errors an access that named an entity or an archetype answers with.
2//!
3//! Every lookup that names an entity answers [`Result`] rather than `Option`.
4//! A handle travels over wires, save files and scope boundaries, so a lookup can
5//! be turned down for reasons a caller has to tell apart: the entity is gone, it
6//! is alive but out of the scope's reach, or its archetype does not carry what
7//! was asked for. `Option` collapses all of them into one silence.
8//!
9//! A structural change is answered the same way. Despawning and relinking are
10//! turned down for reasons of their own - the entity is out of the subtree, it is
11//! shared, it is scaffolding - and a caller that was refused has to know which it
12//! was: the answer to "not yours" is to ask the scope that owns it, and the answer
13//! to "scaffolding" is to stop asking.
14//!
15//! A borrow conflict is deliberately not one of these. It says nothing about the
16//! handle, only that another holder has already claimed the component, and it
17//! stays a panic; see the module documentation of [`crate`].
18
19use crate::Entity;
20
21/// Why a lookup of, or through, an entity handle was turned down.
22///
23/// The handle is carried by every variant, so a caller that reports the error
24/// does not have to thread it alongside.
25#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
26#[non_exhaustive]
27pub enum EntityAccessError {
28    /// No live entity has this handle: it was despawned, or it never named one.
29    #[error("{entity:?} names no live entity")]
30    NotAlive {
31        /// The handle that was looked up.
32        entity: Entity,
33    },
34
35    /// The entity is alive, but out of this scope's reach.
36    #[error("{entity:?} is not visible from {scope:?}")]
37    NotVisible {
38        /// The handle that was looked up.
39        entity: Entity,
40        /// The scope it was looked up from.
41        scope: Entity,
42    },
43
44    /// The entity is visible here, but its archetype does not satisfy the
45    /// query it was looked up with.
46    #[error("{entity:?} does not match the query")]
47    NotMatched {
48        /// The handle that was looked up.
49        entity: Entity,
50    },
51
52    /// The handle was not reserved, so there is no row to spawn on.
53    ///
54    /// A handle from [`crate::World::reserve_entity`] can be spawned on once.
55    /// Spawning on it again, on a handle that was released, or on one that was
56    /// never reserved would leave a row nobody can reach or free.
57    #[error("{entity:?} was not reserved")]
58    NotReserved {
59        /// The handle that was spawned on.
60        entity: Entity,
61    },
62
63    /// The entity is alive, but this scope's subtree does not reach it, so it
64    /// is not the scope's to remove or move.
65    ///
66    /// Despawning and relinking walk the subtree, so what a scope cannot reach
67    /// from inside it is also not its to change. The scope that owns the entity
68    /// - the root, for anything beside this one - is the one to ask.
69    #[error("{entity:?} is outside {scope:?}, so it is not its to move or remove")]
70    NotOwned {
71        /// The handle that was moved or removed.
72        entity: Entity,
73        /// The scope that tried.
74        scope: Entity,
75    },
76
77    /// The entity is visible here, but only the root may move or remove it.
78    ///
79    /// The shared subtree is readable from every scope and rearrangeable by none
80    /// but the root: a scope that reads a resource there is a borrower, not an
81    /// owner.
82    #[error("{entity:?} is shared, so only the root may move or remove it")]
83    NotMovable {
84        /// The handle that was moved or removed.
85        entity: Entity,
86        /// The scope that tried.
87        scope: Entity,
88    },
89
90    /// The entity is one of the tree's two scaffolding entities.
91    ///
92    /// The root and the shared scope are what the tree hangs from: they are
93    /// neither counted nor queried, and they do not move or go.
94    #[error("{entity:?} is scaffolding: it neither moves nor goes")]
95    Scaffolding {
96        /// The handle that was moved or removed.
97        entity: Entity,
98    },
99
100    /// Moving `child` under `parent` would put a scope inside its own subtree,
101    /// which would cut the tree in two.
102    #[error("{child:?} cannot be moved under {parent:?}, which it holds")]
103    WouldCycle {
104        /// The handle that would have moved.
105        child: Entity,
106        /// The parent it would have moved under.
107        parent: Entity,
108    },
109
110    /// The entity's archetype could not be reached.
111    #[error(transparent)]
112    Archetype(#[from] ArchetypeAccessError),
113}
114
115/// Why a lookup by archetype id was turned down.
116///
117/// An archetype id is the row it was registered at, so a stale id keeps naming
118/// a different archetype instead of naming nothing. These variants are what a
119/// caller checks against.
120#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
121#[non_exhaustive]
122pub enum ArchetypeAccessError {
123    /// No archetype was ever registered under this id.
124    #[error("no archetype has id {id}")]
125    NoSuchArchetype {
126        /// The id that was looked up.
127        id: u32,
128    },
129
130    /// The archetype exists, but it holds no row to look at.
131    #[error("archetype {id} holds no row")]
132    Empty {
133        /// The id that was looked up.
134        id: u32,
135    },
136
137    /// The archetype holds rows this scope cannot reach.
138    #[error("archetype {id} holds rows that {scope:?} cannot see")]
139    NotVisible {
140        /// The id that was looked up.
141        id: u32,
142        /// The scope the lookup was made from.
143        scope: Entity,
144    },
145}