unlit_ecs/view.rs
1//! The scope accessors: the read and write entry points of a world.
2//!
3//! A scope names the entity it is rooted at. It sees that entity, everything
4//! below it, and the shared subtree; it does not see its ancestors or its
5//! siblings. There is no way up: a scope cannot name the world it came from, so
6//! holding one is not a way to reach the rest.
7//!
8//! Reading and writing components needs only [`Scope`]. Structural
9//! changes -- spawning, despawning, relinking -- need [`ScopeMut`], whose
10//! operations are confined to the scope's own subtree.
11
12use crate::archetype::{Archetype, ScopeSummary};
13use crate::borrow::Claims;
14use crate::bundle::Bundle;
15use crate::command::Commands;
16use crate::entity::{Entity, Location};
17use crate::error::{ArchetypeAccessError, EntityAccessError};
18use crate::info::{ArchetypeInfo, EntityInfo, WorldInfo};
19use crate::query::{PlanBuilder, Query, QueryBorrow, QueryOne};
20use crate::world::World;
21
22/// A read-only scope: one entity and everything below it.
23#[derive(Clone, Copy)]
24pub struct Scope<'w> {
25 pub(crate) world: &'w World,
26 pub(crate) scope: Entity,
27}
28
29impl core::fmt::Debug for Scope<'_> {
30 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31 // Only the scope is printed: dumping every visible entity would make
32 // one assertion message page through the whole subtree.
33 f.debug_struct("Scope")
34 .field("scope", &self.scope)
35 .finish_non_exhaustive()
36 }
37}
38
39impl<'w> Scope<'w> {
40 pub(crate) fn new(world: &'w World, scope: Entity) -> Self {
41 Self { world, scope }
42 }
43
44 /// The entity this scope is rooted at.
45 #[must_use]
46 pub fn entity(&self) -> Entity {
47 self.scope
48 }
49
50 /// Whether `entity` is visible from this scope.
51 ///
52 /// This is the addressability test: the two roots answer `true` so that
53 /// their components can be read and written like any other entity's, even
54 /// though counting and querying omit them (see [`Scope::len`]).
55 #[must_use]
56 pub fn contains(&self, entity: Entity) -> bool {
57 self.world.visible(self.scope, entity)
58 }
59
60 /// Where a visible entity's components live.
61 ///
62 /// Answers [`EntityAccessError::NotAlive`] for a handle that names no live
63 /// entity, and [`EntityAccessError::NotVisible`] for one that lives outside
64 /// this scope.
65 pub fn location(&self, entity: Entity) -> Result<Location, EntityAccessError> {
66 let location = self.world.location(entity)?;
67 if !self.contains(entity) {
68 return Err(EntityAccessError::NotVisible {
69 entity,
70 scope: self.scope,
71 });
72 }
73 Ok(location)
74 }
75
76 // -- queries -----------------------------------------------------------
77
78 /// Iterate the visible entities that match `Q`.
79 ///
80 /// The query is a type, so which components to hand out and which
81 /// archetypes to visit are written in the turbofish. See [`mod@crate::query`]
82 /// for the items a query is made of.
83 ///
84 /// ````rust
85 /// # use unlit_ecs::{Entity, World, bundle};
86 /// let mut world = World::new();
87 /// world.root_scope_mut().spawn(bundle![1u32]);
88 ///
89 /// for (entity, value) in world.root_scope().query::<(Entity, &u32)>().iter() {
90 /// println!("{entity:?} carries {value}");
91 /// }
92 /// ````
93 ///
94 /// The answer is a holder: it claims the components the query reaches before
95 /// anything is handed out, and releases them when it is dropped. A claim that
96 /// overlaps one already held is reported here, naming where the other was
97 /// taken, rather than at the row it would have reached.
98 #[track_caller]
99 pub fn query<Q: Query>(&self) -> QueryBorrow<'w, Q> {
100 QueryBorrow::new(self.world, self.scope, core::panic::Location::caller())
101 }
102
103 /// What `Q` yields for `entity`, when it is visible here and its archetype
104 /// matches.
105 ///
106 /// This is the one-entity form of [`Scope::query`]: the row the entity is
107 /// stored at is looked up directly instead of the archetypes being walked.
108 /// A lookup that finds nothing says why: the entity is not alive, out of
109 /// this scope's reach, or its row does not satisfy the query. The last one
110 /// covers two cases a caller answers the same way, so it is one error: the
111 /// archetype does not match `Q`, or the row sits outside what this scope
112 /// reaches. A shared row is reachable from every scope, the same by handle
113 /// as it is through [`Scope::query`].
114 ///
115 /// A lookup by handle does not filter the scaffolding: the root and the
116 /// shared scope answer like any other entity, which is what lets the
117 /// resources they carry be read. Iterating and counting go through a scope
118 /// and leave the pair out; see [`Scope::len`].
119 ///
120 /// ````rust
121 /// # use unlit_ecs::{Has, World, bundle};
122 /// let mut world = World::new();
123 /// let entity = world.root_scope_mut().spawn(bundle![1u32]);
124 ///
125 /// assert_eq!(*world.root_scope().query_one::<&u32>(entity).unwrap().get(), 1);
126 /// assert!(world.root_scope().query_one::<Has<u32>>(entity).unwrap().get());
127 /// assert!(!world.root_scope().query_one::<Has<bool>>(entity).unwrap().get());
128 /// assert!(world.root_scope().query_one::<&bool>(entity).is_err());
129 /// ````
130 ///
131 /// The answer is a holder rather than the item itself: it claims the
132 /// components the query reaches on that row while it is built, so the claim
133 /// begins before the item exists and lasts as long as the holder.
134 #[track_caller]
135 pub fn query_one<Q: Query>(
136 &self,
137 entity: Entity,
138 ) -> Result<QueryOne<'w, Q>, EntityAccessError> {
139 let origin = core::panic::Location::caller();
140 let location = self.location(entity)?;
141 let archetype = self.world.archetypes().get(location.archetype())?;
142 if !self.row_visible(archetype, location.row()) {
143 return Err(EntityAccessError::NotMatched { entity });
144 }
145 // A query that cannot resolve its plan on this archetype does not
146 // match it, which is the same answer as a row out of reach.
147 let mut builder = PlanBuilder::new(archetype, Vec::new());
148 let Some(plan) = Q::prepare(&mut builder) else {
149 return Err(EntityAccessError::NotMatched { entity });
150 };
151 // The claims are taken before the holder exists and are released with
152 // it, so a row is never reachable through the plan without a claim on
153 // it.
154 let mut claims = Claims::new(self.world);
155 claims.claim_row(
156 location.archetype(),
157 entity,
158 archetype,
159 builder.asks(),
160 origin,
161 );
162 Ok(QueryOne::new(plan, location.row(), claims))
163 }
164
165 /// The archetype with this id, when this scope sees every row of it.
166 ///
167 /// A partially visible archetype is turned down: handing out its entity
168 /// list would report rows the scope cannot otherwise reach. So is an empty
169 /// one, which has no row to see. A caller that only wants the shape of an
170 /// archetype uses [`Scope::archetype_infos`], whose length is the visible
171 /// one; a caller that has an entity and wants the archetype its row lives
172 /// in uses [`Scope::archetype_of`], which asks about the row rather than
173 /// about the whole archetype.
174 pub fn archetype(&self, id: u32) -> Result<&'w Archetype, ArchetypeAccessError> {
175 let archetype = self.world.archetypes().get(id)?;
176 if archetype.is_empty() {
177 return Err(ArchetypeAccessError::Empty { id });
178 }
179 if !self.fully_visible(archetype) {
180 return Err(ArchetypeAccessError::NotVisible {
181 id,
182 scope: self.scope,
183 });
184 }
185 Ok(archetype)
186 }
187
188 /// The archetype an entity's row lives in, when the entity is visible here.
189 ///
190 /// This is the counterpart of [`Scope::location`]: it answers for one
191 /// visible row, so it also answers for an archetype [`Scope::archetype`]
192 /// turns down. Two scopes whose entities share a component set share the
193 /// archetype and each sees only its own rows of it; asking about one row is
194 /// well defined, while handing out the archetype as a whole is not.
195 ///
196 /// The archetype is the storage, not a view of it: [`Archetype::entities`]
197 /// lists the rows of every scope, the ones this scope cannot see included.
198 /// [`Scope::entity_info`] and [`Scope::entity_infos`] are what filter by
199 /// visibility.
200 pub fn archetype_of(&self, entity: Entity) -> Result<&'w Archetype, EntityAccessError> {
201 let location = self.location(entity)?;
202 self.world
203 .archetypes()
204 .get(location.archetype())
205 .map_err(EntityAccessError::from)
206 }
207
208 /// Every archetype this scope sees in full, in id order.
209 ///
210 /// Empty archetypes are skipped: they hold no row, so there is nothing to
211 /// iterate. The one archetype holding the tree's scaffolding is included,
212 /// since its rows do exist; [`Scope::len`] is what leaves them out of the
213 /// count.
214 pub fn archetypes(&self) -> impl Iterator<Item = &'w Archetype> {
215 self.world
216 .archetypes()
217 .as_slice()
218 .iter()
219 .filter(|archetype| !archetype.is_empty() && self.fully_visible(archetype))
220 }
221
222 // -- the tree ----------------------------------------------------------
223
224 /// The scope of a visible scope.
225 ///
226 /// A scope is an entity like any other, so this is turned down the same way
227 /// [`Scope::location`] is.
228 pub fn scope(&self, scope: Entity) -> Result<Scope<'w>, EntityAccessError> {
229 self.location(scope)?;
230 Ok(Scope::new(self.world, scope))
231 }
232
233 /// The parent of a visible entity.
234 pub fn parent_scope(&self, entity: Entity) -> Result<Entity, EntityAccessError> {
235 self.location(entity)?;
236 self.world.parent_scope(entity)
237 }
238
239 /// The direct children of a visible entity.
240 ///
241 /// Answers [`EntityAccessError::NotAlive`] or
242 /// [`EntityAccessError::NotVisible`] rather than an empty list when the
243 /// entity is not there to ask about.
244 pub fn children(&self, entity: Entity) -> Result<Vec<Entity>, EntityAccessError> {
245 self.location(entity)?;
246 self.world.children(entity)
247 }
248
249 // -- counting and diagnostics ------------------------------------------
250
251 /// The number of visible entities, the two roots excluded.
252 ///
253 /// An empty world is empty; the root and the shared scope are scaffolding
254 /// and are reported by [`WorldInfo::root`] and [`WorldInfo::shared`]
255 /// instead of being counted.
256 #[must_use]
257 pub fn len(&self) -> usize {
258 // The query already skips the scaffolding and applies the visibility
259 // rules, so counting this way keeps the number and the iteration in
260 // agreement for every scope, the root included.
261 self.query::<Entity>().iter().count()
262 }
263
264 /// Whether nothing is visible.
265 #[must_use]
266 pub fn is_empty(&self) -> bool {
267 self.len() == 0
268 }
269
270 /// The scope's shape: visible counts, the component inventory, and the two
271 /// roots.
272 #[must_use]
273 pub fn info(&self) -> WorldInfo {
274 let mut components: Vec<&'static str> = Vec::new();
275 let mut archetypes = 0;
276 for (index, archetype) in self.world.archetypes().as_slice().iter().enumerate() {
277 // Only an archetype with a row this scope can see has a shape to
278 // report: an empty one, or one whose rows all live elsewhere,
279 // would name components that nothing here carries.
280 if self.visible_len(index as u32, archetype) == 0 {
281 continue;
282 }
283 archetypes += 1;
284 components.extend_from_slice(archetype.names());
285 }
286 components.sort_unstable();
287 components.dedup();
288 WorldInfo {
289 entities: self.len(),
290 archetypes,
291 components,
292 root: self.world.root(),
293 shared: self.world.shared(),
294 }
295 }
296
297 /// Every archetype with at least one visible row, in id order.
298 #[must_use]
299 pub fn archetype_infos(&self) -> Vec<ArchetypeInfo> {
300 self.world
301 .archetypes()
302 .as_slice()
303 .iter()
304 .enumerate()
305 .filter_map(|(index, archetype)| {
306 let len = self.visible_len(index as u32, archetype);
307 (len > 0).then(|| ArchetypeInfo {
308 index: index as u32,
309 len,
310 components: archetype.names().to_vec(),
311 })
312 })
313 .collect()
314 }
315
316 /// What a visible entity is called, read from the row that stores it.
317 ///
318 /// A handle rebuilt from bare bits carries no name of its own; the row does.
319 /// See [`World::entity_name`].
320 pub fn entity_name(&self, entity: Entity) -> Result<&'static str, EntityAccessError> {
321 self.location(entity)?;
322 self.world.entity_name(entity)
323 }
324
325 /// A visible entity's location and component names.
326 ///
327 /// The handle reported, and the name beside it, both come from the row that
328 /// stores the entity, so a handle that arrived as bare bits is answered with
329 /// the label its spawn site gave it.
330 pub fn entity_info(&self, entity: Entity) -> Result<EntityInfo, EntityAccessError> {
331 let location = self.location(entity)?;
332 let archetype = self.world.archetypes().get(location.archetype())?;
333 let stored = *archetype
334 .entities()
335 .get(location.row())
336 .expect("a live entity's location names a row of its archetype");
337 Ok(EntityInfo {
338 entity: stored,
339 name: stored.name(),
340 archetype: location.archetype(),
341 row: location.row(),
342 scope: self.world.parent_scope(stored)?,
343 components: archetype.names().to_vec(),
344 })
345 }
346
347 /// At most `limit` visible entities' information, in storage order.
348 #[must_use]
349 pub fn entity_infos(&self, limit: usize) -> Vec<EntityInfo> {
350 let mut infos = Vec::new();
351 for (index, archetype) in self.world.archetypes().as_slice().iter().enumerate() {
352 if !self.archetype_visible(archetype) {
353 continue;
354 }
355 let components: Vec<&'static str> = archetype.names().to_vec();
356 let start = self.world.content_start(index as u32);
357 for (row, &entity) in archetype.entities()[start..].iter().enumerate() {
358 let row = start + row;
359 if !self.row_visible(archetype, row) {
360 continue;
361 }
362 if infos.len() == limit {
363 return infos;
364 }
365 infos.push(EntityInfo {
366 entity,
367 name: entity.name(),
368 archetype: index as u32,
369 row,
370 scope: self
371 .world
372 .parent_scope(entity)
373 .expect("a visible row names a live entity"),
374 components: components.clone(),
375 });
376 }
377 }
378 infos
379 }
380
381 // -- deferred structural changes ---------------------------------------
382
383 /// A queue of structural changes, all of them in this scope.
384 ///
385 /// Takes the scope by value, so the queue outlives it: the scope is a copy of
386 /// a reference, and a queue that borrowed the temporary would be unusable
387 /// in the position it is most wanted.
388 #[must_use]
389 pub fn queue(self) -> Commands<'w> {
390 Commands::new(self.world, self.scope)
391 }
392
393 // -- visibility --------------------------------------------------------
394
395 /// Whether an archetype has any row this scope can see.
396 fn archetype_visible(&self, archetype: &Archetype) -> bool {
397 archetype
398 .scope_summary()
399 .any_visible(self.world, self.scope)
400 }
401
402 /// Whether every row of `archetype` is visible from this scope.
403 fn fully_visible(&self, archetype: &Archetype) -> bool {
404 archetype
405 .scope_summary()
406 .all_visible(self.world, self.scope)
407 }
408
409 /// Whether row `row` of `archetype` is visible.
410 fn row_visible(&self, archetype: &Archetype, row: usize) -> bool {
411 archetype
412 .scope_summary()
413 .row_visible(archetype, self.world, self.scope, row)
414 }
415
416 /// The number of rows of `archetype` this scope can see, its own content
417 /// only: the scaffolding is never counted.
418 fn visible_len(&self, index: u32, archetype: &Archetype) -> usize {
419 // The scaffolding shares an archetype with every bare entity, so its
420 // rows are removed rather than the whole archetype: a caller's own bare
421 // entity may sit in it too. It takes the first rows of that archetype,
422 // which is what makes the subtraction a boundary rather than a filter.
423 let scaffold = self.world.content_start(index);
424 match archetype.scope_summary() {
425 ScopeSummary::Empty => 0,
426 ScopeSummary::Uniform(_) => {
427 if self.fully_visible(archetype) {
428 // The whole archetype is this one scope, so its rows are
429 // the scope's — except a shared row, which reaches the scope
430 // from outside it and so belongs to no other archetype.
431 archetype.len() - scaffold
432 } else {
433 // A scope lives in its parent, which is above it, so the
434 // only row of that archetype it can see is its own -- and
435 // the scaffolding's own row is not content.
436 usize::from(
437 archetype.entities()[scaffold..].contains(&self.scope)
438 && !self.world.is_scaffolding(self.scope),
439 )
440 }
441 }
442 ScopeSummary::Mixed => (scaffold..archetype.len())
443 .filter(|&row| self.row_visible(archetype, row))
444 .count(),
445 }
446 }
447}
448
449/// A mutable scope: one entity and everything below it.
450pub struct ScopeMut<'w> {
451 world: &'w mut World,
452 scope: Entity,
453}
454
455impl<'w> ScopeMut<'w> {
456 pub(crate) fn new(world: &'w mut World, scope: Entity) -> Self {
457 Self { world, scope }
458 }
459
460 /// The entity this scope is rooted at.
461 #[must_use]
462 pub fn entity(&self) -> Entity {
463 self.scope
464 }
465
466 /// The read-only form of this scope.
467 #[must_use]
468 pub fn as_scope(&self) -> Scope<'_> {
469 Scope::new(self.world, self.scope)
470 }
471
472 /// Spawn `bundle` in this scope.
473 ///
474 /// The entity is named after the bundle, which is what its `Debug` prints:
475 /// a type of the caller's own names it after itself, and a `bundle!` after
476 /// the source position that wrote it. A dynamic bundle put together by hand
477 /// carries no name, so an entity spawned from one has none; build it with
478 /// [`DynamicBundle::named`], or spawn it with [`ScopeMut::spawn_named`].
479 ///
480 /// [`DynamicBundle::named`]: crate::DynamicBundle::named
481 pub fn spawn<B: Bundle>(&mut self, bundle: B) -> Entity {
482 self.world.spawn_in(self.scope, bundle)
483 }
484
485 /// Spawn `bundle` in this scope under `name`, whatever the bundle was
486 /// built from.
487 ///
488 /// [`ScopeMut::spawn`] names the entity after the bundle itself, so this is
489 /// for the objects whose name has to say more than that does.
490 pub fn spawn_named<B: Bundle>(&mut self, name: &'static str, bundle: B) -> Entity {
491 self.world.spawn_named_in(self.scope, name, bundle)
492 }
493
494 /// Spawn `bundle` on `entity`, a handle from [`World::reserve_entity`].
495 ///
496 /// Answers [`EntityAccessError::NotReserved`] when the handle is not a live
497 /// reservation: it has already been spawned on, was released, or was never
498 /// reserved to begin with.
499 pub fn spawn_at<B: Bundle>(
500 &mut self,
501 entity: Entity,
502 bundle: B,
503 ) -> Result<(), EntityAccessError> {
504 self.world.spawn_at_in(entity, self.scope, bundle)
505 }
506
507 /// Despawn `entity` and its whole subtree, answering how many entities
508 /// the despawn took with it.
509 ///
510 /// Children are removed before the parent they lived in, so a child's
511 /// despawn callback still sees living ancestors.
512 ///
513 /// Answers [`EntityAccessError::NotAlive`] for a handle that names no live
514 /// entity, `NotOwned` for one outside this scope's subtree, `NotMovable`
515 /// for one the shared subtree only lends this scope - the root is the
516 /// scope that may remove it - and `Scaffolding` for the root and the
517 /// shared scope themselves.
518 pub fn despawn(&mut self, entity: Entity) -> Result<usize, EntityAccessError> {
519 self.world.despawn_in(entity, self.scope)
520 }
521
522 /// Make `child` a child of `parent`.
523 ///
524 /// Both must be owned by this scope, neither may be a root or the shared
525 /// scope, and `parent` may not be inside `child`'s subtree. An entity in
526 /// the shared subtree is the root's to move, so only a root scope relinks
527 /// it.
528 ///
529 /// The same errors as [`ScopeMut::despawn`], plus
530 /// [`EntityAccessError::WouldCycle`] when `parent` is `child` itself or
531 /// sits inside its subtree: that move would cut the tree in two.
532 pub fn link(&mut self, child: Entity, parent: Entity) -> Result<(), EntityAccessError> {
533 self.world.link_in(child, parent, self.scope)
534 }
535
536 /// Detach `child` from its parent and put it in this scope.
537 ///
538 /// An entity already in this scope has nowhere to move to, and that is
539 /// answered with `Ok(())` rather than an error: what the caller asked for
540 /// is already true.
541 ///
542 /// The same errors as [`ScopeMut::despawn`], plus
543 /// [`EntityAccessError::WouldCycle`] when `child` is this scope itself,
544 /// which would make it its own parent.
545 pub fn unlink(&mut self, child: Entity) -> Result<(), EntityAccessError> {
546 self.world.unlink_in(child, self.scope, self.scope)
547 }
548
549 /// The mutable scope of a visible scope.
550 pub fn scope_mut(&mut self, scope: Entity) -> Result<ScopeMut<'_>, EntityAccessError> {
551 self.as_scope().location(scope)?;
552 Ok(ScopeMut::new(self.world, scope))
553 }
554
555 /// Apply every queued command.
556 ///
557 /// See [`World::apply`] for what a failing command is answered with.
558 pub fn apply(&mut self) -> Result<(), EntityAccessError> {
559 self.world.apply()
560 }
561}