Skip to main content

goud_engine/ecs/query/fetch/
component_ref.rs

1//! Component reference query implementations: `&T` and `&mut T`.
2//!
3//! Provides [`WorldQuery`] implementations for immutable and mutable component
4//! references, along with [`MutState`] and [`WriteAccess`] helper types.
5
6use std::collections::BTreeSet;
7
8use crate::ecs::archetype::Archetype;
9use crate::ecs::component::ComponentId;
10use crate::ecs::entity::Entity;
11use crate::ecs::World;
12
13use super::traits::{ReadOnlyWorldQuery, WorldQuery};
14
15// =============================================================================
16// Component Reference Implementation (&T)
17// =============================================================================
18
19/// Query for an immutable component reference.
20///
21/// `&T` queries fetch a reference to component `T` for each matching entity.
22/// This is one of the most common query types.
23///
24/// # Archetype Matching
25///
26/// An archetype matches `&T` if it contains component `T`. The query returns
27/// `None` for entities that don't have the component.
28///
29/// # Parallel Safety
30///
31/// `&T` is a read-only query and implements [`ReadOnlyWorldQuery`]. Multiple
32/// `&T` queries for the same component can run in parallel, and `&T` can run
33/// alongside `&U` for different components.
34///
35/// # Example
36///
37/// ```
38/// use goud_engine::ecs::{World, Component, Entity};
39/// use goud_engine::ecs::query::WorldQuery;
40///
41/// #[derive(Debug, Clone, Copy, PartialEq)]
42/// struct Position { x: f32, y: f32 }
43/// impl Component for Position {}
44///
45/// let mut world = World::new();
46/// let entity = world.spawn_empty();
47/// world.insert(entity, Position { x: 1.0, y: 2.0 });
48///
49/// // Initialize query state
50/// let state = <&Position>::init_state(&world);
51///
52/// // Fetch component reference
53/// let pos = <&Position>::fetch(&state, &world, entity);
54/// assert!(pos.is_some());
55/// assert_eq!(pos.unwrap(), &Position { x: 1.0, y: 2.0 });
56/// ```
57///
58/// # Access Conflicts
59///
60/// - `&T` conflicts with `&mut T` (read-write conflict)
61/// - `&T` does NOT conflict with `&T` (multiple readers allowed)
62/// - `&T` does NOT conflict with `&U` where U ≠ T
63impl<T: crate::ecs::Component> WorldQuery for &T {
64    type Item<'w> = &'w T;
65    type State = ComponentId;
66
67    #[inline]
68    fn init_state(_world: &World) -> Self::State {
69        ComponentId::of::<T>()
70    }
71
72    #[inline]
73    fn component_access(state: &Self::State) -> BTreeSet<ComponentId> {
74        let mut set = BTreeSet::new();
75        set.insert(*state);
76        set
77    }
78
79    #[inline]
80    fn matches_archetype(state: &Self::State, archetype: &Archetype) -> bool {
81        archetype.has_component(*state)
82    }
83
84    #[inline]
85    fn fetch<'w>(state: &Self::State, world: &'w World, entity: Entity) -> Option<Self::Item<'w>> {
86        // Avoid unused variable warning while maintaining intent
87        let _ = state;
88
89        // Use World::get which already checks entity liveness
90        world.get::<T>(entity)
91    }
92}
93
94impl<T: crate::ecs::Component> ReadOnlyWorldQuery for &T {}
95
96// =============================================================================
97// Mutable Component Reference Implementation (&mut T)
98// =============================================================================
99
100/// Marker type for tracking write access to a component.
101///
102/// Used to distinguish between read and write access in conflict detection.
103/// This allows the query system to detect when two queries would violate
104/// Rust's aliasing rules (one mutable + any other access to same component).
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
106pub struct WriteAccess(pub ComponentId);
107
108/// Query state for mutable component access.
109///
110/// Contains both the component ID and a marker indicating this is a write access.
111/// Used for accurate access conflict detection.
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub struct MutState {
114    /// The component ID being accessed.
115    pub component_id: ComponentId,
116}
117
118impl MutState {
119    /// Creates a new mutable access state for the given component type.
120    #[inline]
121    pub fn of<T: crate::ecs::Component>() -> Self {
122        Self {
123            component_id: ComponentId::of::<T>(),
124        }
125    }
126}
127
128/// Query for a mutable component reference.
129///
130/// `&mut T` queries fetch a mutable reference to component `T` for each matching
131/// entity. This allows modifying component data in place.
132///
133/// # Archetype Matching
134///
135/// An archetype matches `&mut T` if it contains component `T`. The query returns
136/// `None` for entities that don't have the component.
137///
138/// # Access Conflicts
139///
140/// Mutable queries have strict access requirements:
141///
142/// - `&mut T` **conflicts** with `&T` (write-read conflict)
143/// - `&mut T` **conflicts** with `&mut T` (write-write conflict)
144/// - `&mut T` does **NOT** conflict with `&U` or `&mut U` where U ≠ T
145///
146/// The scheduler uses this information to prevent parallel execution of
147/// conflicting systems.
148///
149/// # Thread Safety
150///
151/// `&mut T` does **NOT** implement [`ReadOnlyWorldQuery`]. This means:
152///
153/// 1. Systems using `&mut T` cannot run in parallel with other systems accessing `T`
154/// 2. The query iterator enforces exclusive access at runtime
155///
156/// # Example
157///
158/// ```
159/// use goud_engine::ecs::{World, Component, Entity};
160/// use goud_engine::ecs::query::WorldQuery;
161///
162/// #[derive(Debug, Clone, Copy, PartialEq)]
163/// struct Health(f32);
164/// impl Component for Health {}
165///
166/// let mut world = World::new();
167/// let entity = world.spawn_empty();
168/// world.insert(entity, Health(100.0));
169///
170/// // Initialize query state
171/// let state = <&mut Health>::init_state(&world);
172///
173/// // Fetch mutable component reference
174/// if let Some(health) = <&mut Health>::fetch_mut(&state, &mut world, entity) {
175///     health.0 -= 10.0; // Modify in place
176/// }
177///
178/// // Verify modification
179/// assert_eq!(world.get::<Health>(entity), Some(&Health(90.0)));
180/// ```
181///
182/// # Important Notes
183///
184/// - The `fetch` method returns `None` because mutable access requires a mutable
185///   world reference. Use `fetch_mut` for mutable queries.
186/// - The query system enforces that only one mutable access to a component exists
187///   at any time, preventing aliasing issues.
188impl<T: crate::ecs::Component> WorldQuery for &mut T {
189    type Item<'w> = &'w mut T;
190    type State = MutState;
191
192    #[inline]
193    fn init_state(_world: &World) -> Self::State {
194        MutState::of::<T>()
195    }
196
197    #[inline]
198    fn component_access(state: &Self::State) -> BTreeSet<ComponentId> {
199        let mut set = BTreeSet::new();
200        set.insert(state.component_id);
201        set
202    }
203
204    #[inline]
205    fn matches_archetype(state: &Self::State, archetype: &Archetype) -> bool {
206        archetype.has_component(state.component_id)
207    }
208
209    /// Returns `None` because mutable access requires `fetch_mut`.
210    ///
211    /// This is intentional - the immutable world reference cannot provide
212    /// mutable component access without violating Rust's aliasing rules.
213    #[inline]
214    fn fetch<'w>(
215        _state: &Self::State,
216        _world: &'w World,
217        _entity: Entity,
218    ) -> Option<Self::Item<'w>> {
219        // Cannot provide mutable access from immutable world reference
220        // Callers must use fetch_mut for mutable queries
221        None
222    }
223
224    #[inline]
225    fn fetch_mut<'w>(
226        state: &Self::State,
227        world: &'w mut World,
228        entity: Entity,
229    ) -> Option<Self::Item<'w>> {
230        // Avoid unused variable warning while maintaining intent
231        let _ = state;
232
233        // Use World::get_mut which already checks entity liveness
234        world.get_mut::<T>(entity)
235    }
236}
237
238// NOTE: &mut T does NOT implement ReadOnlyWorldQuery
239// This is intentional - mutable queries conflict with all other access to the same component