Skip to main content

goud_engine/ecs/query/
iter.rs

1//! Query iterators: [`QueryIter`] (immutable) and [`QueryIterMut`] (mutable).
2
3use crate::ecs::archetype::ArchetypeId;
4use crate::ecs::entity::Entity;
5use crate::ecs::World;
6
7use super::fetch::WorldQuery;
8use super::query_type::Query;
9
10// =============================================================================
11// Query Iterator
12// =============================================================================
13
14/// Iterator over query results (immutable access).
15///
16/// When the query has an archetype cache, the iterator only visits cached
17/// archetype indices instead of scanning every archetype in the world.
18pub struct QueryIter<'w, 'q, Q: WorldQuery, F: WorldQuery> {
19    /// Reference to the query.
20    query: &'q Query<Q, F>,
21    /// Reference to the world.
22    world: &'w World,
23    /// Current position in the iteration (either archetype graph index or
24    /// index into the cached archetype list, depending on `cached`).
25    cursor: usize,
26    /// Current entity index within the current archetype.
27    entity_index: usize,
28    /// Whether we are iterating over cached archetype indices.
29    cached: bool,
30}
31
32impl<'w, 'q, Q: WorldQuery, F: WorldQuery> QueryIter<'w, 'q, Q, F> {
33    /// Creates a new query iterator.
34    #[inline]
35    pub(crate) fn new(query: &'q Query<Q, F>, world: &'w World) -> Self {
36        let cached = query.archetype_cache.is_some();
37        Self {
38            query,
39            world,
40            cursor: 0,
41            entity_index: 0,
42            cached,
43        }
44    }
45
46    /// Resolves the current cursor to an actual archetype graph index.
47    #[inline]
48    fn current_archetype_index(&self) -> Option<usize> {
49        if self.cached {
50            // SAFETY: `cached` is only true when archetype_cache is Some
51            let cache = self.query.archetype_cache.as_ref()?;
52            let indices = cache.archetype_indices();
53            if self.cursor < indices.len() {
54                Some(indices[self.cursor])
55            } else {
56                None
57            }
58        } else {
59            let total = self.world.archetypes().len();
60            if self.cursor < total {
61                Some(self.cursor)
62            } else {
63                None
64            }
65        }
66    }
67}
68
69impl<'w, 'q, Q: WorldQuery, F: WorldQuery> Iterator for QueryIter<'w, 'q, Q, F> {
70    type Item = Q::Item<'w>;
71
72    fn next(&mut self) -> Option<Self::Item> {
73        loop {
74            let graph_index = self.current_archetype_index()?;
75            let id = ArchetypeId::new(graph_index as u32);
76            let archetype = self.world.archetypes().get(id)?;
77
78            // When not using cache, skip non-matching archetypes
79            if !self.cached && !self.query.matches_archetype(archetype) {
80                self.cursor += 1;
81                self.entity_index = 0;
82                continue;
83            }
84
85            let entities = archetype.entities();
86
87            if self.entity_index >= entities.len() {
88                self.cursor += 1;
89                self.entity_index = 0;
90                continue;
91            }
92
93            let entity = entities[self.entity_index];
94            self.entity_index += 1;
95
96            // Try to fetch data for this entity
97            if let Some(item) = self.query.get(self.world, entity) {
98                return Some(item);
99            }
100            // If fetch failed (entity despawned, etc.), continue to next
101        }
102    }
103}
104
105// =============================================================================
106// Query Iterator Mut
107// =============================================================================
108
109/// Iterator over query results (mutable access).
110///
111/// Due to Rust's borrowing rules, this iterator collects matching entities
112/// first, then yields them one at a time with mutable access.
113pub struct QueryIterMut<'w, 'q, Q: WorldQuery, F: WorldQuery> {
114    /// Reference to the query.
115    query: &'q Query<Q, F>,
116    /// Mutable reference to the world.
117    world: &'w mut World,
118    /// Collected entities that match the query.
119    entities: Vec<Entity>,
120    /// Current index in the entities vec.
121    current_index: usize,
122}
123
124impl<'w, 'q, Q: WorldQuery, F: WorldQuery> QueryIterMut<'w, 'q, Q, F> {
125    /// Creates a new mutable query iterator.
126    #[inline]
127    pub(crate) fn new(query: &'q Query<Q, F>, world: &'w mut World) -> Self {
128        // Collect all matching entities first
129        let mut entities = Vec::new();
130
131        if let Some(cache) = &query.archetype_cache {
132            // Use cached archetype indices -- skip the full scan
133            let archetypes = world.archetypes();
134            for &idx in cache.archetype_indices() {
135                let id = ArchetypeId::new(idx as u32);
136                if let Some(archetype) = archetypes.get(id) {
137                    entities.extend_from_slice(archetype.entities());
138                }
139            }
140        } else {
141            // Fallback: scan all archetypes
142            for archetype in world.archetypes().iter() {
143                if query.matches_archetype(archetype) {
144                    entities.extend_from_slice(archetype.entities());
145                }
146            }
147        }
148
149        Self {
150            query,
151            world,
152            entities,
153            current_index: 0,
154        }
155    }
156}
157
158impl<'w, 'q, Q: WorldQuery, F: WorldQuery> Iterator for QueryIterMut<'w, 'q, Q, F> {
159    type Item = Q::Item<'w>;
160
161    fn next(&mut self) -> Option<Self::Item> {
162        loop {
163            if self.current_index >= self.entities.len() {
164                return None;
165            }
166
167            let entity = self.entities[self.current_index];
168            self.current_index += 1;
169
170            // SAFETY: We ensure exclusive access to the world, and the query
171            // type Q determines what components are accessed. The borrow
172            // checker ensures we don't have overlapping mutable borrows.
173            // We need to reborrow the world for each iteration.
174            let world_ptr = self.world as *mut World;
175            let world_ref: &'w mut World = unsafe { &mut *world_ptr };
176
177            // Check filter first
178            if F::fetch(&self.query.filter_state, world_ref, entity).is_none() {
179                continue;
180            }
181
182            // Then fetch mutable data
183            if let Some(item) = Q::fetch_mut(&self.query.query_state, world_ref, entity) {
184                return Some(item);
185            }
186            // If fetch failed (entity despawned, etc.), continue to next
187        }
188    }
189}