Skip to main content

goud_engine/ecs/query/fetch/
optional.rs

1//! Optional query support: [`WorldQuery`] implementation for `Option<Q>`.
2//!
3//! Allows querying for components that may or may not be present on an entity.
4//! `Option<&T>` always matches every archetype and returns `Some(Some(&T))`
5//! when the component is present, or `Some(None)` when it is absent.
6
7use std::collections::BTreeSet;
8
9use crate::ecs::archetype::Archetype;
10use crate::ecs::component::ComponentId;
11use crate::ecs::entity::Entity;
12use crate::ecs::World;
13
14use super::traits::{ReadOnlyWorldQuery, WorldQuery};
15
16// =============================================================================
17// Option<Q> WorldQuery Implementation
18// =============================================================================
19
20/// Optional query wrapper.
21///
22/// `Option<Q>` always matches every archetype. When the inner query `Q` can
23/// fetch data for an entity, the result is `Some(Q::Item)`. When it cannot,
24/// the result is `None`. In both cases, the outer `fetch` returns `Some(...)`,
25/// so the entity is never skipped by the query system.
26///
27/// # Use Cases
28///
29/// - Querying for a component that only some entities have
30/// - Avoiding the need for separate queries when a component is optional
31/// - Combining required and optional components in tuple queries
32///
33/// # Example
34///
35/// ```rust,ignore
36/// // Query all entities with Position, optionally fetching Velocity
37/// // (&Position, Option<&Velocity>)
38/// // - Entities with both: (pos, Some(vel))
39/// // - Entities with only Position: (pos, None)
40/// ```
41impl<Q: WorldQuery> WorldQuery for Option<Q> {
42    type Item<'w> = Option<Q::Item<'w>>;
43    type State = Q::State;
44
45    #[inline]
46    fn init_state(world: &World) -> Self::State {
47        Q::init_state(world)
48    }
49
50    #[inline]
51    fn component_access(state: &Self::State) -> BTreeSet<ComponentId> {
52        // Forward to inner query so mutable optional queries (Option<&mut T>)
53        // correctly declare write access for conflict detection.
54        Q::component_access(state)
55    }
56
57    #[inline]
58    fn matches_archetype(_state: &Self::State, _archetype: &Archetype) -> bool {
59        // Optional queries match every archetype — the component is optional.
60        true
61    }
62
63    #[inline]
64    fn fetch<'w>(state: &Self::State, world: &'w World, entity: Entity) -> Option<Self::Item<'w>> {
65        // Always return Some. The inner Option indicates presence/absence.
66        Some(Q::fetch(state, world, entity))
67    }
68
69    #[inline]
70    fn fetch_mut<'w>(
71        state: &Self::State,
72        world: &'w mut World,
73        entity: Entity,
74    ) -> Option<Self::Item<'w>> {
75        // Always return Some. The inner Option indicates presence/absence.
76        Some(Q::fetch_mut(state, world, entity))
77    }
78}
79
80// Option<Q> is read-only when the inner query is read-only.
81impl<Q: ReadOnlyWorldQuery> ReadOnlyWorldQuery for Option<Q> {}