Skip to main content

elevator_core/query/
iter.rs

1//! Query builder and iterator for entity iteration.
2
3use std::marker::PhantomData;
4
5use slotmap::basic::Keys;
6
7use crate::entity::EntityId;
8use crate::world::World;
9
10use super::fetch::WorldQuery;
11use super::filter::QueryFilter;
12
13/// Builder for mutable extension queries.
14///
15/// Created by [`World::query_ext_mut()`]. Collects matching entity IDs
16/// upfront (keys snapshot) to avoid borrow conflicts, then provides
17/// [`for_each_mut`](Self::for_each_mut) for mutable iteration.
18pub struct ExtQueryMut<'w, T: 'static + Send + Sync, F: QueryFilter = ()> {
19    /// The world being queried (mutable).
20    world: &'w mut World,
21    /// Snapshot of matching entity IDs.
22    ids: Vec<EntityId>,
23    /// Marker for the extension type and filter.
24    _marker: PhantomData<(T, F)>,
25}
26
27impl<'w, T: 'static + Send + Sync, F: QueryFilter> ExtQueryMut<'w, T, F> {
28    /// Create a new mutable extension query builder.
29    pub(crate) fn new(world: &'w mut World) -> Self {
30        let ids: Vec<EntityId> = world
31            .alive_keys()
32            .filter(|&id| {
33                world.ext_map::<T>().is_some_and(|m| m.contains_key(id)) && F::matches(world, id)
34            })
35            .collect();
36        Self {
37            world,
38            ids,
39            _marker: PhantomData,
40        }
41    }
42
43    /// Entity IDs matching this query.
44    #[must_use]
45    pub fn ids(&self) -> &[EntityId] {
46        &self.ids
47    }
48
49    /// Number of matching entities.
50    #[must_use]
51    pub const fn count(&self) -> usize {
52        self.ids.len()
53    }
54
55    /// Apply a closure to each matching entity's extension data mutably.
56    pub fn for_each_mut(&mut self, mut f: impl FnMut(EntityId, &mut T)) {
57        for &id in &self.ids {
58            if let Some(val) = self.world.get_ext_mut::<T>(id) {
59                f(id, val);
60            }
61        }
62    }
63}
64
65/// Builder for constructing and executing queries.
66///
67/// Created by [`World::query()`]. Chain `.with::<T>()` and `.without::<T>()`
68/// to add filters, then call `.iter()` or `.get(id)` to execute.
69pub struct QueryBuilder<'w, Q: WorldQuery, F: QueryFilter = ()> {
70    /// The world being queried.
71    world: &'w World,
72    /// Type-level query and filter state.
73    _marker: PhantomData<(Q, F)>,
74}
75
76impl<'w, Q: WorldQuery, F: QueryFilter> QueryBuilder<'w, Q, F> {
77    /// Create a new query builder.
78    pub(crate) const fn new(world: &'w World) -> Self {
79        Self {
80            world,
81            _marker: PhantomData,
82        }
83    }
84
85    /// Add a `With<T>` filter — only match entities that have built-in component `T`.
86    #[must_use]
87    pub const fn with<T: 'static>(self) -> QueryBuilder<'w, Q, (F, super::filter::With<T>)>
88    where
89        (F, super::filter::With<T>): QueryFilter,
90    {
91        QueryBuilder::new(self.world)
92    }
93
94    /// Add a `Without<T>` filter — exclude entities that have built-in component `T`.
95    #[must_use]
96    pub const fn without<T: 'static>(self) -> QueryBuilder<'w, Q, (F, super::filter::Without<T>)>
97    where
98        (F, super::filter::Without<T>): QueryFilter,
99    {
100        QueryBuilder::new(self.world)
101    }
102
103    /// Add an `ExtWith<T>` filter — only match entities with extension component `T`.
104    #[must_use]
105    pub const fn ext_with<T: 'static + Send + Sync>(
106        self,
107    ) -> QueryBuilder<'w, Q, (F, super::filter::ExtWith<T>)>
108    where
109        (F, super::filter::ExtWith<T>): QueryFilter,
110    {
111        QueryBuilder::new(self.world)
112    }
113
114    /// Add an `ExtWithout<T>` filter — exclude entities with extension component `T`.
115    #[must_use]
116    pub const fn ext_without<T: 'static + Send + Sync>(
117        self,
118    ) -> QueryBuilder<'w, Q, (F, super::filter::ExtWithout<T>)>
119    where
120        (F, super::filter::ExtWithout<T>): QueryFilter,
121    {
122        QueryBuilder::new(self.world)
123    }
124
125    /// Execute the query and iterate matching entities.
126    #[must_use]
127    pub fn iter(self) -> QueryIter<'w, Q, F> {
128        QueryIter {
129            world: self.world,
130            keys: self.world.alive_keys(),
131            _marker: PhantomData,
132        }
133    }
134
135    /// Fetch a single entity by ID.
136    #[must_use]
137    pub fn get(self, id: EntityId) -> Option<Q::Item<'w>> {
138        if !Q::contains(self.world, id) || !F::matches(self.world, id) {
139            return None;
140        }
141        Q::fetch(self.world, id)
142    }
143}
144
145/// Iterator over entities matching a query and its filters.
146pub struct QueryIter<'w, Q: WorldQuery, F: QueryFilter> {
147    /// The world being iterated.
148    world: &'w World,
149    /// Iterator over alive entity keys.
150    keys: Keys<'w, EntityId, ()>,
151    /// Type-level query and filter state.
152    _marker: PhantomData<(Q, F)>,
153}
154
155impl<'w, Q: WorldQuery, F: QueryFilter> Iterator for QueryIter<'w, Q, F> {
156    type Item = Q::Item<'w>;
157
158    fn next(&mut self) -> Option<Self::Item> {
159        loop {
160            let id = self.keys.next()?;
161            if !Q::contains(self.world, id) {
162                continue;
163            }
164            if !F::matches(self.world, id) {
165                continue;
166            }
167            if let Some(item) = Q::fetch(self.world, id) {
168                return Some(item);
169            }
170        }
171    }
172}
173
174impl<'w, Q: WorldQuery, F: QueryFilter> QueryIter<'w, Q, F> {
175    /// Count the number of matching entities without collecting.
176    #[must_use]
177    pub fn count_matches(self) -> usize {
178        self.count()
179    }
180
181    /// Return `true` if any entity matches the given predicate.
182    pub fn any_match(mut self, predicate: impl FnMut(Q::Item<'w>) -> bool) -> bool {
183        self.any(predicate)
184    }
185
186    /// Return `true` if all matching entities satisfy the predicate.
187    ///
188    /// Returns `true` for an empty query (vacuous truth).
189    pub fn all_match(mut self, predicate: impl FnMut(Q::Item<'w>) -> bool) -> bool {
190        self.all(predicate)
191    }
192
193    /// Find the first entity matching the predicate.
194    pub fn find_match(
195        mut self,
196        predicate: impl FnMut(&Q::Item<'w>) -> bool,
197    ) -> Option<Q::Item<'w>> {
198        self.find(predicate)
199    }
200}