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> {}