Skip to main content

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}