khora_data/ecs/
query.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::ecs::{
16    page::{AnyVec, ComponentPage},
17    Component, EntityId, World,
18};
19use std::{any::TypeId, marker::PhantomData};
20
21// ------------------------- //
22// ---- WorldQuery Part ---- //
23// ------------------------- //
24
25/// A trait implemented by types that can be used to query data from the `World`.
26///
27/// This "sealed" trait provides the necessary information for the query engine to
28/// find the correct pages and safely access component data. It is implemented for
29/// component references (`&T`, `&mut T`), `EntityId`, filters like `Without<T>`,
30/// and tuples of other `WorldQuery` types.
31pub trait WorldQuery {
32    /// The type of item that the query iterator will yield (e.g., `(&'a Position, &'a mut Velocity)`).
33    type Item<'a>;
34
35    /// Returns the sorted list of `TypeId`s for the components to be INCLUDED in the query.
36    /// This signature is used to find `ComponentPage`s that contain all these components.
37    fn type_ids() -> Vec<TypeId>;
38
39    /// Returns the sorted list of `TypeId`s for components to be EXCLUDED from the query.
40    /// Used to filter out pages that contain these components.
41    fn without_type_ids() -> Vec<TypeId> {
42        Vec::new()
43    }
44
45    /// Fetches the query's item from a specific row in a `ComponentPage`.
46    ///
47    /// # Safety
48    ///
49    /// This function is unsafe because the caller (the `Query` iterator) must guarantee
50    /// several invariants:
51    /// 1. The page pointed to by `page_ptr` is valid and matches the query's signature.
52    /// 2. `row_index` is a valid index within the page's columns.
53    /// 3. Aliasing rules are not violated (e.g., no two `&mut T` to the same data).
54    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a>;
55}
56
57// Implementation for a query of a single, immutable component reference.
58impl<T: Component> WorldQuery for &T {
59    type Item<'a> = &'a T;
60
61    fn type_ids() -> Vec<TypeId> {
62        vec![TypeId::of::<T>()]
63    }
64
65    /// Fetches a reference to the component `T` from the specified row.
66    ///
67    /// # Safety
68    /// The caller MUST guarantee that the page contains a column for component `T`
69    /// and that `row_index` is in bounds.
70    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
71        // 1. Get a reference to the `ComponentPage`.
72        let page = &*page_ptr;
73
74        // 2. Get the type-erased column for the component `T`.
75        // We can unwrap because the caller guarantees the column exists.
76        let column: &dyn AnyVec = &**page.columns.get(&TypeId::of::<T>()).unwrap();
77
78        // 3. Downcast the column to its concrete `Vec<T>` type.
79        // First, cast to `&dyn Any`, then `downcast_ref`.
80        let vec: &Vec<T> = column.as_any().downcast_ref::<Vec<T>>().unwrap();
81
82        // 4. Get the component from the vector at the specified row.
83        // We use `get_unchecked` for performance, as the caller guarantees the
84        // index is in bounds. This avoids a bounds check.
85        vec.get_unchecked(row_index)
86    }
87}
88
89// Implementation for a query of a single, mutable component reference.
90impl<T: Component> WorldQuery for &mut T {
91    type Item<'a> = &'a mut T;
92
93    fn type_ids() -> Vec<TypeId> {
94        vec![TypeId::of::<T>()]
95    }
96
97    /// Fetches a mutable reference to the component `T` from the specified row.
98    ///
99    /// # Safety
100    /// The caller MUST guarantee that:
101    /// 1. The page contains a column for component `T`.
102    /// 2. `row_index` is in bounds.
103    /// 3. No other mutable reference to this specific component exists at the same time.
104    ///    The query engine must enforce this.
105    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
106        // UNSAFE: We cast the const pointer to a mutable one.
107        // This is safe ONLY if the query engine guarantees no other access.
108        let page = &mut *(page_ptr as *mut ComponentPage);
109        let column = page.columns.get_mut(&TypeId::of::<T>()).unwrap();
110        let vec = column.as_any_mut().downcast_mut::<Vec<T>>().unwrap();
111        vec.get_unchecked_mut(row_index)
112    }
113}
114
115// Implementation for a query of a 2-item tuple.
116// This is now generic over any two types that implement `WorldQuery`.
117impl<Q1: WorldQuery, Q2: WorldQuery> WorldQuery for (Q1, Q2) {
118    type Item<'a> = (Q1::Item<'a>, Q2::Item<'a>);
119
120    fn type_ids() -> Vec<TypeId> {
121        let mut ids = Q1::type_ids();
122        ids.extend(Q2::type_ids());
123        // Sorting is crucial to maintain a canonical signature.
124        ids.sort();
125        // We also remove duplicates, in case of queries like `(&T, &T)`.
126        ids.dedup();
127        ids
128    }
129
130    fn without_type_ids() -> Vec<TypeId> {
131        let mut ids = Q1::without_type_ids();
132        ids.extend(Q2::without_type_ids());
133        ids.sort();
134        ids.dedup();
135        ids
136    }
137
138    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
139        // Fetch data for each part of the tuple individually.
140        let item1 = Q1::fetch(page_ptr, row_index);
141        let item2 = Q2::fetch(page_ptr, row_index);
142        (item1, item2)
143    }
144}
145
146// Implementation for a query of a 3-item tuple.
147impl<Q1: WorldQuery, Q2: WorldQuery, Q3: WorldQuery> WorldQuery for (Q1, Q2, Q3) {
148    type Item<'a> = (Q1::Item<'a>, Q2::Item<'a>, Q3::Item<'a>);
149
150    fn type_ids() -> Vec<TypeId> {
151        let mut ids = Q1::type_ids();
152        ids.extend(Q2::type_ids());
153        ids.extend(Q3::type_ids());
154        ids.sort();
155        ids.dedup();
156        ids
157    }
158
159    fn without_type_ids() -> Vec<TypeId> {
160        let mut ids = Q1::without_type_ids();
161        ids.extend(Q2::without_type_ids());
162        ids.extend(Q3::without_type_ids());
163        ids.sort();
164        ids.dedup();
165        ids
166    }
167
168    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
169        // Fetch data for each part of the tuple individually.
170        let item1 = Q1::fetch(page_ptr, row_index);
171        let item2 = Q2::fetch(page_ptr, row_index);
172        let item3 = Q3::fetch(page_ptr, row_index);
173        (item1, item2, item3)
174    }
175}
176
177// Implementation for a query of a 4-item tuple.
178impl<Q1: WorldQuery, Q2: WorldQuery, Q3: WorldQuery, Q4: WorldQuery> WorldQuery
179    for (Q1, Q2, Q3, Q4)
180{
181    type Item<'a> = (Q1::Item<'a>, Q2::Item<'a>, Q3::Item<'a>, Q4::Item<'a>);
182
183    fn type_ids() -> Vec<TypeId> {
184        let mut ids = Q1::type_ids();
185        ids.extend(Q2::type_ids());
186        ids.extend(Q3::type_ids());
187        ids.extend(Q4::type_ids());
188        ids.sort();
189        ids.dedup();
190        ids
191    }
192
193    fn without_type_ids() -> Vec<TypeId> {
194        let mut ids = Q1::without_type_ids();
195        ids.extend(Q2::without_type_ids());
196        ids.extend(Q3::without_type_ids());
197        ids.extend(Q4::without_type_ids());
198        ids.sort();
199        ids.dedup();
200        ids
201    }
202
203    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
204        (
205            Q1::fetch(page_ptr, row_index),
206            Q2::fetch(page_ptr, row_index),
207            Q3::fetch(page_ptr, row_index),
208            Q4::fetch(page_ptr, row_index),
209        )
210    }
211}
212
213// To fetch an entity's ID, we need to access the page's own entity list.
214// We also need to query for the entity ID itself.
215impl WorldQuery for EntityId {
216    type Item<'a> = EntityId;
217
218    // EntityId is not a component, it doesn't have a TypeId in the page signature.
219    fn type_ids() -> Vec<TypeId> {
220        Vec::new()
221    }
222
223    // It doesn't have a `without` filter either.
224    // The default implementation is sufficient.
225
226    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
227        let page = &*page_ptr;
228        // The entity ID is fetched from the page's own list of entities.
229        *page.entities.get_unchecked(row_index)
230    }
231}
232
233// -------------------- //
234// ---- Query Part ---- //
235// -------------------- //
236
237/// An iterator that yields the results of a `WorldQuery`.
238///
239/// This struct is created by the [`World::query()`] method. It holds a reference
240/// to the world and iterates through all `ComponentPage`s that match the query's
241/// signature, fetching the requested data for each entity in those pages.
242pub struct Query<'a, Q: WorldQuery> {
243    /// A raw pointer to the world. Using a pointer avoids lifetime variance issues
244    /// and gives us the flexibility to provide either mutable or immutable access.
245    world_ptr: *const World,
246
247    /// The list of page indices that match this query.
248    matching_page_indices: Vec<u32>,
249
250    /// The index of the current page we are iterating through.
251    current_page_index: usize,
252
253    /// The index of the next row to fetch within the current page.
254    current_row_index: usize,
255
256    /// A marker to associate this iterator with the lifetime `'a` and the query type `Q`.
257    /// It tells Rust that our iterator behaves as if it's borrowing `&'a Q` from the world.
258    _phantom: PhantomData<(&'a (), Q)>,
259}
260
261impl<'a, Q: WorldQuery> Query<'a, Q> {
262    /// (Internal) Creates a new `Query` iterator.
263    ///
264    /// This is intended to be called only by `World::query()`.
265    /// It takes the world and the list of matching pages as arguments.
266    pub(crate) fn new(world: &'a World, matching_page_indices: Vec<u32>) -> Self {
267        Self {
268            world_ptr: world as *const _,
269            matching_page_indices,
270            current_page_index: 0,
271            current_row_index: 0,
272            _phantom: PhantomData,
273        }
274    }
275}
276
277impl<'a, Q: WorldQuery> Iterator for Query<'a, Q> {
278    /// The type of item yielded by this iterator, as defined by the `WorldQuery` trait.
279    type Item = Q::Item<'a>;
280
281    /// Advances the iterator and returns the next item.
282    fn next(&mut self) -> Option<Self::Item> {
283        loop {
284            // 1. Check if there are any pages left to iterate through.
285            if self.current_page_index >= self.matching_page_indices.len() {
286                return None; // No more pages, iteration is finished.
287            }
288
289            // Unsafe block because we are dereferencing a raw pointer.
290            // This is safe because the `Query` is only created from a valid `World` reference.
291            let world = unsafe { &*self.world_ptr };
292
293            // 2. Get the current page.
294            let page_id = self.matching_page_indices[self.current_page_index];
295            let page = &world.pages[page_id as usize];
296
297            // 3. Check if there are rows left in the current page.
298            if self.current_row_index < page.row_count() {
299                // There are rows left, so we can fetch the data.
300                let item = unsafe {
301                    // This is safe because we've already matched the page signature
302                    // and we are iterating within the page's bounds.
303                    Q::fetch(page as *const _, self.current_row_index)
304                };
305
306                // Advance the row index for the next call.
307                self.current_row_index += 1;
308                return Some(item);
309            } else {
310                // 4. No more rows in this page. Move to the next page.
311                self.current_page_index += 1;
312                self.current_row_index = 0; // Reset the row index for the new page.
313                                            // The `loop` will then re-evaluate with the new page index.
314            }
315        }
316    }
317}
318
319/// A `WorldQuery` filter that matches entities that do NOT have component `T`.
320///
321/// This is used as a marker in a query tuple to exclude entities. For example,
322/// `Query<(&Position, Without<Velocity>)>` will iterate over all entities
323/// that have a `Position` but no `Velocity`.
324pub struct Without<T: Component>(PhantomData<T>);
325
326// `Without<T>` itself doesn't fetch any data, so its `WorldQuery` implementation
327// is mostly empty. It acts as a signal to the query engine.
328impl<T: Component> WorldQuery for Without<T> {
329    /// This query item is a zero-sized unit type, as it fetches no data.
330    type Item<'a> = ();
331
332    /// A `Without` filter does not add any component types to the query's main
333    /// signature for page matching. The filtering is handled separately.
334    fn type_ids() -> Vec<TypeId> {
335        Vec::new() // Returns an empty Vec.
336    }
337
338    /// This is the key part of the filter: it returns the TypeId of the component to filter out.
339    fn without_type_ids() -> Vec<TypeId> {
340        // This is the key change: it returns the TypeId of the component to filter out.
341        vec![TypeId::of::<T>()]
342    }
343
344    /// Fetches nothing. Returns a unit type `()`.
345    unsafe fn fetch<'a>(_page_ptr: *const ComponentPage, _row_index: usize) -> Self::Item<'a> {
346        // This function will be called but its result is ignored.
347    }
348}