1use crate::{
2 archetype::{
3 Archetype, ArchetypeColumnAccess, ArchetypeDynamicColumnAccess, ArchetypeDynamicColumnItem,
4 ArchetypeDynamicColumnIter, ArchetypeError,
5 },
6 entity::{Entity, EntityDenseMap},
7 world::World,
8 Component,
9};
10use intuicio_data::type_hash::TypeHash;
11use std::{
12 collections::{HashMap, HashSet},
13 error::Error,
14 marker::PhantomData,
15};
16
17#[derive(Debug)]
18pub enum QueryError {
19 Archetype(ArchetypeError),
20 TryingToReadUnavailableType { type_hash: TypeHash },
21 TryingToWriteUnavailableType { type_hash: TypeHash },
22}
23
24impl Error for QueryError {}
25
26impl From<ArchetypeError> for QueryError {
27 fn from(value: ArchetypeError) -> Self {
28 Self::Archetype(value)
29 }
30}
31
32impl std::fmt::Display for QueryError {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 Self::Archetype(archetype) => write!(f, "World archetype: {}", archetype),
36 Self::TryingToReadUnavailableType { type_hash } => {
37 write!(f, "Trying to read unavailable type: {:?}", type_hash)
38 }
39 Self::TryingToWriteUnavailableType { type_hash } => {
40 write!(f, "Trying to write unavailable type: {:?}", type_hash)
41 }
42 }
43 }
44}
45
46pub struct Query<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>>(
47 PhantomData<fn() -> &'a Fetch>,
48);
49
50impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> Default
51 for Query<'a, LOCKING, Fetch>
52{
53 fn default() -> Self {
54 Self(Default::default())
55 }
56}
57
58impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> Clone
59 for Query<'a, LOCKING, Fetch>
60{
61 fn clone(&self) -> Self {
62 *self
63 }
64}
65
66impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> Copy
67 for Query<'a, LOCKING, Fetch>
68{
69}
70
71impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> Query<'a, LOCKING, Fetch> {
72 pub fn query(&self, world: &'a World) -> TypedQueryIter<'a, LOCKING, Fetch> {
73 world.query::<'a, LOCKING, Fetch>()
74 }
75}
76
77pub struct Lookup<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>>(
78 PhantomData<fn() -> &'a Fetch>,
79);
80
81impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> Default
82 for Lookup<'a, LOCKING, Fetch>
83{
84 fn default() -> Self {
85 Self(Default::default())
86 }
87}
88
89impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> Clone
90 for Lookup<'a, LOCKING, Fetch>
91{
92 fn clone(&self) -> Self {
93 *self
94 }
95}
96
97impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> Copy
98 for Lookup<'a, LOCKING, Fetch>
99{
100}
101
102impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> Lookup<'a, LOCKING, Fetch> {
103 pub fn lookup(
104 &self,
105 world: &'a World,
106 entities: impl IntoIterator<Item = Entity> + 'a,
107 ) -> TypedLookupIter<'a, LOCKING, Fetch> {
108 world.lookup::<'a, LOCKING, Fetch>(entities)
109 }
110
111 pub fn lookup_access(&self, world: &'a World) -> TypedLookupAccess<'a, LOCKING, Fetch> {
112 world.lookup_access::<'a, LOCKING, Fetch>()
113 }
114}
115
116pub trait TypedQueryFetch<'a, const LOCKING: bool> {
117 type Value;
118 type Access;
119
120 fn does_accept_archetype(archetype: &Archetype) -> bool;
121 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError>;
122 fn fetch(access: &mut Self::Access) -> Option<Self::Value>;
123
124 #[allow(unused_variables)]
125 fn unique_access(output: &mut HashSet<TypeHash>) {}
126}
127
128pub trait TypedLookupFetch<'a, const LOCKING: bool> {
129 type Value;
130 type Access;
131
132 fn try_access(archetype: &'a Archetype) -> Option<Self::Access>;
133 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value>;
134
135 #[allow(unused_variables)]
136 fn unique_access(output: &mut HashSet<TypeHash>) {}
137}
138
139impl<const LOCKING: bool> TypedQueryFetch<'_, LOCKING> for () {
140 type Value = ();
141 type Access = ();
142
143 fn does_accept_archetype(_: &Archetype) -> bool {
144 true
145 }
146
147 fn access(_: &Archetype) -> Result<Self::Access, QueryError> {
148 Ok(())
149 }
150
151 fn fetch(_: &mut Self::Access) -> Option<Self::Value> {
152 Some(())
153 }
154}
155
156impl<const LOCKING: bool> TypedLookupFetch<'_, LOCKING> for () {
157 type Value = ();
158 type Access = ();
159
160 fn try_access(_: &Archetype) -> Option<Self::Access> {
161 Some(())
162 }
163
164 fn fetch(_: &mut Self::Access, _: Entity) -> Option<Self::Value> {
165 Some(())
166 }
167}
168
169impl<'a, const LOCKING: bool> TypedQueryFetch<'a, LOCKING> for Entity {
170 type Value = Entity;
171 type Access = Box<dyn Iterator<Item = Entity> + 'a>;
172
173 fn does_accept_archetype(_: &Archetype) -> bool {
174 true
175 }
176
177 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError> {
178 Ok(Box::new(archetype.entities().iter()))
179 }
180
181 fn fetch(access: &mut Self::Access) -> Option<Self::Value> {
182 access.next()
183 }
184}
185
186impl<'a, const LOCKING: bool> TypedLookupFetch<'a, LOCKING> for Entity {
187 type Value = Entity;
188 type Access = &'a EntityDenseMap;
189
190 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
191 Some(archetype.entities())
192 }
193
194 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
195 if access.contains(entity) {
196 Some(entity)
197 } else {
198 None
199 }
200 }
201}
202
203impl<'a, const LOCKING: bool, T: Component> TypedQueryFetch<'a, LOCKING> for &'a T {
204 type Value = &'a T;
205 type Access = Box<dyn Iterator<Item = &'a T> + 'a>;
206
207 fn does_accept_archetype(archetype: &Archetype) -> bool {
208 archetype.has_type(TypeHash::of::<T>())
209 }
210
211 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError> {
212 Ok(Box::new(archetype.column_read_iter::<LOCKING, T>()?))
213 }
214
215 fn fetch(access: &mut Self::Access) -> Option<Self::Value> {
216 access.next()
217 }
218}
219
220impl<'a, const LOCKING: bool, T: Component> TypedLookupFetch<'a, LOCKING> for &'a T {
221 type Value = &'a T;
222 type Access = (&'a EntityDenseMap, ArchetypeColumnAccess<'a, LOCKING, T>);
223
224 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
225 if archetype.has_type(TypeHash::of::<T>()) {
226 Some((
227 archetype.entities(),
228 archetype.column::<LOCKING, T>(false).ok()?,
229 ))
230 } else {
231 None
232 }
233 }
234
235 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
236 if let Some(index) = access.0.index_of(entity) {
237 access
238 .1
239 .read(index)
240 .map(|value| unsafe { std::mem::transmute(value) })
241 } else {
242 None
243 }
244 }
245}
246
247impl<'a, const LOCKING: bool, T: Component> TypedQueryFetch<'a, LOCKING> for &'a mut T {
248 type Value = &'a mut T;
249 type Access = Box<dyn Iterator<Item = &'a mut T> + 'a>;
250
251 fn does_accept_archetype(archetype: &Archetype) -> bool {
252 archetype.has_type(TypeHash::of::<T>())
253 }
254
255 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError> {
256 Ok(Box::new(archetype.column_write_iter::<LOCKING, T>()?))
257 }
258
259 fn fetch(access: &mut Self::Access) -> Option<Self::Value> {
260 access.next()
261 }
262
263 fn unique_access(output: &mut HashSet<TypeHash>) {
264 output.insert(TypeHash::of::<T>());
265 }
266}
267
268impl<'a, const LOCKING: bool, T: Component> TypedLookupFetch<'a, LOCKING> for &'a mut T {
269 type Value = &'a mut T;
270 type Access = (&'a EntityDenseMap, ArchetypeColumnAccess<'a, LOCKING, T>);
271
272 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
273 if archetype.has_type(TypeHash::of::<T>()) {
274 Some((
275 archetype.entities(),
276 archetype.column::<LOCKING, T>(true).ok()?,
277 ))
278 } else {
279 None
280 }
281 }
282
283 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
284 if let Some(index) = access.0.index_of(entity) {
285 access
286 .1
287 .write(index)
288 .map(|value| unsafe { std::mem::transmute(value) })
289 } else {
290 None
291 }
292 }
293
294 fn unique_access(output: &mut HashSet<TypeHash>) {
295 output.insert(TypeHash::of::<T>());
296 }
297}
298
299impl<'a, const LOCKING: bool, T: Component> TypedQueryFetch<'a, LOCKING> for Option<&'a T> {
300 type Value = Option<&'a T>;
301 type Access = Option<Box<dyn Iterator<Item = &'a T> + 'a>>;
302
303 fn does_accept_archetype(_: &Archetype) -> bool {
304 true
305 }
306
307 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError> {
308 match archetype.column_read_iter::<LOCKING, T>().ok() {
309 Some(value) => Ok(Some(Box::new(value))),
310 None => Ok(None),
311 }
312 }
313
314 fn fetch(access: &mut Self::Access) -> Option<Self::Value> {
315 match access {
316 Some(access) => Some(access.next()),
318 None => Some(None),
319 }
320 }
321}
322
323impl<'a, const LOCKING: bool, T: Component> TypedLookupFetch<'a, LOCKING> for Option<&'a T> {
324 type Value = Option<&'a T>;
325 type Access = Option<(&'a EntityDenseMap, ArchetypeColumnAccess<'a, LOCKING, T>)>;
326
327 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
328 match archetype.column::<LOCKING, T>(false).ok() {
329 Some(value) => Some(Some((archetype.entities(), value))),
330 None => Some(None),
331 }
332 }
333
334 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
335 match access {
336 Some(access) => Some(if let Some(index) = access.0.index_of(entity) {
338 access
339 .1
340 .read(index)
341 .map(|value| unsafe { std::mem::transmute(value) })
342 } else {
343 None
344 }),
345 None => Some(None),
346 }
347 }
348}
349
350impl<'a, const LOCKING: bool, T: Component> TypedQueryFetch<'a, LOCKING> for Option<&'a mut T> {
351 type Value = Option<&'a mut T>;
352 type Access = Option<Box<dyn Iterator<Item = &'a mut T> + 'a>>;
353
354 fn does_accept_archetype(_: &Archetype) -> bool {
355 true
356 }
357
358 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError> {
359 match archetype.column_write_iter::<LOCKING, T>().ok() {
360 Some(value) => Ok(Some(Box::new(value))),
361 None => Ok(None),
362 }
363 }
364
365 fn fetch(access: &mut Self::Access) -> Option<Self::Value> {
366 match access {
367 Some(access) => Some(access.next()),
369 None => Some(None),
370 }
371 }
372
373 fn unique_access(output: &mut HashSet<TypeHash>) {
374 output.insert(TypeHash::of::<T>());
375 }
376}
377
378impl<'a, const LOCKING: bool, T: Component> TypedLookupFetch<'a, LOCKING> for Option<&'a mut T> {
379 type Value = Option<&'a mut T>;
380 type Access = Option<(&'a EntityDenseMap, ArchetypeColumnAccess<'a, LOCKING, T>)>;
381
382 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
383 match archetype.column::<LOCKING, T>(true).ok() {
384 Some(value) => Some(Some((archetype.entities(), value))),
385 None => Some(None),
386 }
387 }
388
389 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
390 match access {
391 Some(access) => Some(if let Some(index) = access.0.index_of(entity) {
393 access
394 .1
395 .write(index)
396 .map(|value| unsafe { std::mem::transmute(value) })
397 } else {
398 None
399 }),
400 None => Some(None),
401 }
402 }
403
404 fn unique_access(output: &mut HashSet<TypeHash>) {
405 output.insert(TypeHash::of::<T>());
406 }
407}
408
409pub struct Include<T: Component>(PhantomData<fn() -> T>);
410
411impl<const LOCKING: bool, T: Component> TypedQueryFetch<'_, LOCKING> for Include<T> {
412 type Value = ();
413 type Access = ();
414
415 fn does_accept_archetype(archetype: &Archetype) -> bool {
416 archetype.has_type(TypeHash::of::<T>())
417 }
418
419 fn access(_: &Archetype) -> Result<Self::Access, QueryError> {
420 Ok(())
421 }
422
423 fn fetch(_: &mut Self::Access) -> Option<Self::Value> {
424 Some(())
425 }
426}
427
428impl<'a, const LOCKING: bool, T: Component> TypedLookupFetch<'a, LOCKING> for Include<T> {
429 type Value = ();
430 type Access = &'a EntityDenseMap;
431
432 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
433 if archetype.has_type(TypeHash::of::<T>()) {
434 Some(archetype.entities())
435 } else {
436 None
437 }
438 }
439
440 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
441 if access.contains(entity) {
442 Some(())
443 } else {
444 None
445 }
446 }
447}
448
449pub struct Exclude<T: Component>(PhantomData<fn() -> T>);
450
451impl<const LOCKING: bool, T: Component> TypedQueryFetch<'_, LOCKING> for Exclude<T> {
452 type Value = ();
453 type Access = ();
454
455 fn does_accept_archetype(archetype: &Archetype) -> bool {
456 !archetype.has_type(TypeHash::of::<T>())
457 }
458
459 fn access(_: &Archetype) -> Result<Self::Access, QueryError> {
460 Ok(())
461 }
462
463 fn fetch(_: &mut Self::Access) -> Option<Self::Value> {
464 Some(())
465 }
466}
467
468impl<'a, const LOCKING: bool, T: Component> TypedLookupFetch<'a, LOCKING> for Exclude<T> {
469 type Value = ();
470 type Access = &'a EntityDenseMap;
471
472 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
473 if !archetype.has_type(TypeHash::of::<T>()) {
474 Some(archetype.entities())
475 } else {
476 None
477 }
478 }
479
480 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
481 if access.contains(entity) {
482 Some(())
483 } else {
484 None
485 }
486 }
487}
488
489pub struct Update<T: Component>(PhantomData<fn() -> T>);
490
491pub struct UpdatedAccess<'a, T>(Entity, &'a mut T);
492
493impl<'a, T> UpdatedAccess<'a, T> {
494 pub fn entity(&self) -> Entity {
495 self.0
496 }
497
498 pub fn read(&'a self) -> &'a T {
499 self.1
500 }
501
502 pub fn write(&'a mut self) -> &'a mut T {
503 self.1
504 }
505
506 pub fn notify(&self, world: &World) {
507 world.update::<T>(self.0);
508 }
509
510 pub fn write_notified(&'a mut self, world: &World) -> &'a mut T {
511 self.notify(world);
512 self.write()
513 }
514}
515
516impl<'a, const LOCKING: bool, T: Component> TypedQueryFetch<'a, LOCKING> for Update<T> {
517 type Value = UpdatedAccess<'a, T>;
518 type Access = Box<dyn Iterator<Item = (Entity, &'a mut T)> + 'a>;
519
520 fn does_accept_archetype(archetype: &Archetype) -> bool {
521 archetype.has_type(TypeHash::of::<T>())
522 }
523
524 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError> {
525 Ok(Box::new(
526 archetype
527 .entities()
528 .iter()
529 .zip(archetype.column_write_iter::<LOCKING, T>()?),
530 ))
531 }
532
533 fn fetch(access: &mut Self::Access) -> Option<Self::Value> {
534 access
535 .next()
536 .map(|(entity, data)| UpdatedAccess(entity, data))
537 }
538
539 fn unique_access(output: &mut HashSet<TypeHash>) {
540 output.insert(TypeHash::of::<T>());
541 }
542}
543
544impl<'a, const LOCKING: bool, T: Component> TypedLookupFetch<'a, LOCKING> for Update<T> {
545 type Value = UpdatedAccess<'a, T>;
546 type Access = (&'a EntityDenseMap, ArchetypeColumnAccess<'a, LOCKING, T>);
547
548 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
549 if archetype.has_type(TypeHash::of::<T>()) {
550 Some((
551 archetype.entities(),
552 archetype.column::<LOCKING, T>(true).ok()?,
553 ))
554 } else {
555 None
556 }
557 }
558
559 fn fetch(access: &mut Self::Access, entity: Entity) -> Option<Self::Value> {
560 if let Some(index) = access.0.index_of(entity) {
561 access
562 .1
563 .write(index)
564 .map(|value| unsafe { std::mem::transmute(value) })
565 .map(|data| UpdatedAccess(entity, data))
566 } else {
567 None
568 }
569 }
570
571 fn unique_access(output: &mut HashSet<TypeHash>) {
572 output.insert(TypeHash::of::<T>());
573 }
574}
575
576macro_rules! impl_typed_query_fetch_tuple {
577 ($($type:ident),+) => {
578 impl<'a, const LOCKING: bool, $($type: TypedQueryFetch<'a, LOCKING>),+> TypedQueryFetch<'a, LOCKING> for ($($type,)+) {
579 type Value = ($($type::Value,)+);
580 type Access = ($($type::Access,)+);
581
582 fn does_accept_archetype(archetype: &Archetype) -> bool {
583 $($type::does_accept_archetype(archetype))&&+
584 }
585
586 fn access(archetype: &'a Archetype) -> Result<Self::Access, QueryError> {
587 Ok(($($type::access(archetype)?,)+))
588 }
589
590 fn fetch(access: &mut Self::Access) -> Option<Self::Value> {
591 #[allow(non_snake_case)]
592 let ($($type,)+) = access;
593 Some(($($type::fetch($type)?,)+))
594 }
595
596 fn unique_access(output: &mut HashSet<TypeHash>) {
597 $(
598 $type::unique_access(output);
599 )+
600 }
601 }
602 };
603}
604
605impl_typed_query_fetch_tuple!(A);
606impl_typed_query_fetch_tuple!(A, B);
607impl_typed_query_fetch_tuple!(A, B, C);
608impl_typed_query_fetch_tuple!(A, B, C, D);
609impl_typed_query_fetch_tuple!(A, B, C, D, E);
610impl_typed_query_fetch_tuple!(A, B, C, D, E, F);
611impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G);
612impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H);
613impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I);
614impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I, J);
615impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K);
616impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
617impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
618impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
619impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
620impl_typed_query_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
621
622macro_rules! impl_typed_lookup_fetch_tuple {
623 ($($type:ident),+) => {
624 impl<'a, const LOCKING: bool, $($type: TypedLookupFetch<'a, LOCKING>),+> TypedLookupFetch<'a, LOCKING> for ($($type,)+) {
625 type Value = ($($type::Value,)+);
626 type Access = ($($type::Access,)+);
627
628 fn try_access(archetype: &'a Archetype) -> Option<Self::Access> {
629 Some(($($type::try_access(archetype)?,)+))
630 }
631
632 fn fetch(access: & mut Self::Access, entity: Entity) -> Option<Self::Value> {
633 #[allow(non_snake_case)]
634 let ($($type,)+) = access;
635 Some(($($type::fetch($type, entity)?,)+))
636 }
637
638 fn unique_access(output: &mut HashSet<TypeHash>) {
639 $(
640 $type::unique_access(output);
641 )+
642 }
643 }
644 };
645}
646
647impl_typed_lookup_fetch_tuple!(A);
648impl_typed_lookup_fetch_tuple!(A, B);
649impl_typed_lookup_fetch_tuple!(A, B, C);
650impl_typed_lookup_fetch_tuple!(A, B, C, D);
651impl_typed_lookup_fetch_tuple!(A, B, C, D, E);
652impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F);
653impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G);
654impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H);
655impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I);
656impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I, J);
657impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K);
658impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
659impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
660impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
661impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
662impl_typed_lookup_fetch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
663
664pub struct TypedQueryIter<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> {
665 archetypes: Vec<&'a Archetype>,
666 index: usize,
667 access: Option<Fetch::Access>,
668 _phantom: PhantomData<fn() -> Fetch>,
669}
670
671impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>>
672 TypedQueryIter<'a, LOCKING, Fetch>
673{
674 pub fn new(world: &'a World) -> Self {
675 Self {
676 archetypes: world
677 .archetypes()
678 .filter(|archetype| Fetch::does_accept_archetype(archetype))
679 .collect(),
680 index: 0,
681 access: None,
682 _phantom: PhantomData,
683 }
684 }
685}
686
687impl<'a, const LOCKING: bool, Fetch: TypedQueryFetch<'a, LOCKING>> Iterator
688 for TypedQueryIter<'a, LOCKING, Fetch>
689{
690 type Item = Fetch::Value;
691
692 fn next(&mut self) -> Option<Self::Item> {
693 while self.index < self.archetypes.len() {
694 match self.access.as_mut() {
695 Some(access) => {
696 let item = Fetch::fetch(access);
697 if item.is_none() {
698 self.access = None;
699 self.index += 1;
700 continue;
701 }
702 return item;
703 }
704 None => {
705 if let Some(archetype) = self.archetypes.get(self.index) {
706 self.access = Some(Fetch::access(archetype).unwrap());
707 } else {
708 self.index += 1;
709 }
710 continue;
711 }
712 }
713 }
714 None
715 }
716}
717
718pub struct TypedLookupIter<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> {
719 access: Vec<Fetch::Access>,
720 entities: Box<dyn Iterator<Item = Entity> + 'a>,
721 _phantom: PhantomData<fn() -> Fetch>,
722}
723
724impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>>
725 TypedLookupIter<'a, LOCKING, Fetch>
726{
727 pub fn new(world: &'a World, entities: impl IntoIterator<Item = Entity> + 'a) -> Self {
728 Self {
729 access: world
730 .archetypes()
731 .filter_map(|archetype| Fetch::try_access(archetype))
732 .collect(),
733 entities: Box::new(entities.into_iter()),
734 _phantom: PhantomData,
735 }
736 }
737}
738
739impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> Iterator
740 for TypedLookupIter<'a, LOCKING, Fetch>
741{
742 type Item = Fetch::Value;
743
744 fn next(&mut self) -> Option<Self::Item> {
745 let entity = self.entities.next()?;
746 for access in &mut self.access {
747 if let Some(result) = Fetch::fetch(access, entity) {
748 return Some(result);
749 }
750 }
751 None
752 }
753}
754
755pub struct TypedLookupAccess<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>> {
756 access: Vec<Fetch::Access>,
757 _phantom: PhantomData<fn() -> Fetch>,
758}
759
760impl<'a, const LOCKING: bool, Fetch: TypedLookupFetch<'a, LOCKING>>
761 TypedLookupAccess<'a, LOCKING, Fetch>
762{
763 pub fn new(world: &'a World) -> Self {
764 Self {
765 access: world
766 .archetypes()
767 .filter_map(|archetype| Fetch::try_access(archetype))
768 .collect(),
769 _phantom: PhantomData,
770 }
771 }
772
773 pub fn access(&mut self, entity: Entity) -> Option<Fetch::Value> {
774 for access in &mut self.access {
775 if let Some(result) = Fetch::fetch(access, entity) {
776 return Some(result);
777 }
778 }
779 None
780 }
781}
782
783#[derive(Debug)]
784enum DynamicQueryFilterMode {
785 Read,
786 Write,
787 Include,
788 Exclude,
789}
790
791#[derive(Debug, Default)]
792pub struct DynamicQueryFilter {
793 filter: HashMap<TypeHash, DynamicQueryFilterMode>,
794}
795
796impl DynamicQueryFilter {
797 pub fn from_raw(
798 read: &[TypeHash],
799 write: &[TypeHash],
800 include: &[TypeHash],
801 exclude: &[TypeHash],
802 ) -> Self {
803 Self {
804 filter: read
805 .iter()
806 .copied()
807 .map(|type_hash| (type_hash, DynamicQueryFilterMode::Read))
808 .chain(
809 write
810 .iter()
811 .copied()
812 .map(|type_hash| (type_hash, DynamicQueryFilterMode::Write)),
813 )
814 .chain(
815 include
816 .iter()
817 .copied()
818 .map(|type_hash| (type_hash, DynamicQueryFilterMode::Include)),
819 )
820 .chain(
821 exclude
822 .iter()
823 .copied()
824 .map(|type_hash| (type_hash, DynamicQueryFilterMode::Exclude)),
825 )
826 .collect(),
827 }
828 }
829
830 pub fn read<T>(self) -> Self {
831 self.read_raw(TypeHash::of::<T>())
832 }
833
834 pub fn read_raw(mut self, type_hash: TypeHash) -> Self {
835 self.filter.insert(type_hash, DynamicQueryFilterMode::Read);
836 self
837 }
838
839 pub fn write<T>(self) -> Self {
840 self.write_raw(TypeHash::of::<T>())
841 }
842
843 pub fn write_raw(mut self, type_hash: TypeHash) -> Self {
844 self.filter.insert(type_hash, DynamicQueryFilterMode::Write);
845 self
846 }
847
848 pub fn include<T>(self) -> Self {
849 self.include_raw(TypeHash::of::<T>())
850 }
851
852 pub fn include_raw(mut self, type_hash: TypeHash) -> Self {
853 self.filter
854 .insert(type_hash, DynamicQueryFilterMode::Include);
855 self
856 }
857
858 pub fn exclude<T>(self) -> Self {
859 self.exclude_raw(TypeHash::of::<T>())
860 }
861
862 pub fn exclude_raw(mut self, type_hash: TypeHash) -> Self {
863 self.filter
864 .insert(type_hash, DynamicQueryFilterMode::Exclude);
865 self
866 }
867
868 pub fn does_accept_archetype(&self, archetype: &Archetype) -> bool {
869 self.filter.iter().all(|(type_hash, mode)| match mode {
870 DynamicQueryFilterMode::Read
871 | DynamicQueryFilterMode::Write
872 | DynamicQueryFilterMode::Include => archetype.has_type(*type_hash),
873 DynamicQueryFilterMode::Exclude => !archetype.has_type(*type_hash),
874 })
875 }
876
877 fn columns(&self) -> Vec<(TypeHash, bool)> {
878 self.columns_iter().collect()
879 }
880
881 fn columns_iter(&self) -> impl Iterator<Item = (TypeHash, bool)> + '_ {
882 self.filter
883 .iter()
884 .filter_map(|(type_hash, mode)| match mode {
885 DynamicQueryFilterMode::Read => Some((*type_hash, false)),
886 DynamicQueryFilterMode::Write => Some((*type_hash, true)),
887 _ => None,
888 })
889 }
890
891 pub fn unique_access(&self, output: &mut HashSet<TypeHash>) {
892 for (type_hash, filter) in &self.filter {
893 if matches!(filter, DynamicQueryFilterMode::Write) {
894 output.insert(*type_hash);
895 }
896 }
897 }
898
899 pub fn query<'a, const LOCKING: bool>(
900 &self,
901 world: &'a World,
902 ) -> DynamicQueryIter<'a, LOCKING> {
903 world.dynamic_query::<LOCKING>(self)
904 }
905}
906
907pub struct DynamicQueryItem<'a> {
908 entity: Entity,
909 columns: Vec<ArchetypeDynamicColumnItem<'a>>,
910}
911
912impl<'a> DynamicQueryItem<'a> {
913 pub fn entity(&self) -> Entity {
914 self.entity
915 }
916
917 pub fn read<T>(&self) -> Result<&ArchetypeDynamicColumnItem<'a>, QueryError> {
918 self.read_raw(TypeHash::of::<T>())
919 }
920
921 pub fn read_raw(
922 &self,
923 type_hash: TypeHash,
924 ) -> Result<&ArchetypeDynamicColumnItem<'a>, QueryError> {
925 self.columns
926 .iter()
927 .find(|column| column.type_hash() == type_hash)
928 .ok_or(QueryError::TryingToReadUnavailableType { type_hash })
929 }
930
931 pub fn write<T>(&mut self) -> Result<&mut ArchetypeDynamicColumnItem<'a>, QueryError> {
932 self.write_raw(TypeHash::of::<T>())
933 }
934
935 pub fn write_raw(
936 &mut self,
937 type_hash: TypeHash,
938 ) -> Result<&mut ArchetypeDynamicColumnItem<'a>, QueryError> {
939 self.columns
940 .iter_mut()
941 .find(|column| column.type_hash() == type_hash)
942 .ok_or(QueryError::TryingToWriteUnavailableType { type_hash })
943 }
944}
945
946pub struct DynamicQueryIter<'a, const LOCKING: bool> {
947 columns: Vec<(TypeHash, bool)>,
949 archetypes: Vec<&'a Archetype>,
950 index: usize,
951 access: Option<(
952 Box<dyn Iterator<Item = Entity> + 'a>,
953 Vec<ArchetypeDynamicColumnIter<'a, LOCKING>>,
954 )>,
955}
956
957impl<'a, const LOCKING: bool> DynamicQueryIter<'a, LOCKING> {
958 pub fn new(filter: &DynamicQueryFilter, world: &'a World) -> Self {
959 Self {
960 columns: filter.columns(),
961 archetypes: world
962 .archetypes()
963 .filter(|archetype| filter.does_accept_archetype(archetype))
964 .collect(),
965 index: 0,
966 access: None,
967 }
968 }
969}
970
971impl<'a, const LOCKING: bool> Iterator for DynamicQueryIter<'a, LOCKING> {
972 type Item = DynamicQueryItem<'a>;
973
974 fn next(&mut self) -> Option<Self::Item> {
975 while self.index < self.archetypes.len() {
976 match self.access.as_mut() {
977 Some((entities, columns)) => {
978 let entity = entities.next();
979 match columns
980 .iter_mut()
981 .map(|access| access.next())
982 .collect::<Option<_>>()
983 .and_then(|columns| Some((entity?, columns)))
984 {
985 Some((entity, columns)) => {
986 return Some(DynamicQueryItem { entity, columns });
987 }
988 None => {
989 self.access = None;
990 self.index += 1;
991 continue;
992 }
993 }
994 }
995 None => {
996 if let Some(archetype) = self.archetypes.get(self.index) {
997 self.access = Some((
998 Box::new(archetype.entities().iter()),
999 self.columns
1000 .iter()
1001 .copied()
1002 .map(|(type_hash, unique)| {
1003 archetype.dynamic_column_iter(type_hash, unique).unwrap()
1004 })
1005 .collect(),
1006 ));
1007 } else {
1008 self.index += 1;
1009 }
1010 continue;
1011 }
1012 }
1013 }
1014 None
1015 }
1016}
1017
1018pub struct DynamicLookupIter<'a, const LOCKING: bool> {
1019 columns: Vec<(TypeHash, bool)>,
1021 access: Vec<(
1022 &'a EntityDenseMap,
1023 ArchetypeDynamicColumnAccess<'a, LOCKING>,
1024 )>,
1025 entities: Box<dyn Iterator<Item = Entity> + 'a>,
1026}
1027
1028impl<'a, const LOCKING: bool> DynamicLookupIter<'a, LOCKING> {
1029 pub fn new(
1030 filter: &DynamicQueryFilter,
1031 world: &'a World,
1032 entities: impl IntoIterator<Item = Entity> + 'a,
1033 ) -> Self {
1034 Self {
1035 columns: filter.columns(),
1036 access: world
1037 .archetypes()
1038 .filter(|archetype| filter.does_accept_archetype(archetype))
1039 .flat_map(|archetype| {
1040 filter.columns_iter().filter_map(|(type_hash, unique)| {
1041 Some((
1042 archetype.entities(),
1043 archetype.dynamic_column(type_hash, unique).ok()?,
1044 ))
1045 })
1046 })
1047 .collect(),
1048 entities: Box::new(entities.into_iter()),
1049 }
1050 }
1051}
1052
1053impl<'a, const LOCKING: bool> Iterator for DynamicLookupIter<'a, LOCKING> {
1054 type Item = DynamicQueryItem<'a>;
1055
1056 fn next(&mut self) -> Option<Self::Item> {
1057 let entity = self.entities.next()?;
1058 let columns = self
1059 .columns
1060 .iter()
1061 .map(|(type_hash, unique)| {
1062 self.access
1063 .iter()
1064 .find(|(map, access)| {
1065 map.contains(entity)
1066 && access.info().type_hash() == *type_hash
1067 && access.is_unique() == *unique
1068 })
1069 .and_then(|(map, access)| unsafe {
1070 std::mem::transmute(access.dynamic_item(map.index_of(entity).unwrap()).ok())
1071 })
1072 })
1073 .collect::<Option<Vec<_>>>()?;
1074 Some(DynamicQueryItem { entity, columns })
1075 }
1076}
1077
1078pub struct DynamicLookupAccess<'a, const LOCKING: bool> {
1079 columns: Vec<(TypeHash, bool)>,
1081 access: Vec<(
1082 &'a EntityDenseMap,
1083 ArchetypeDynamicColumnAccess<'a, LOCKING>,
1084 )>,
1085}
1086
1087impl<'a, const LOCKING: bool> DynamicLookupAccess<'a, LOCKING> {
1088 pub fn new(filter: &DynamicQueryFilter, world: &'a World) -> Self {
1089 Self {
1090 columns: filter.columns(),
1091 access: world
1092 .archetypes()
1093 .filter(|archetype| filter.does_accept_archetype(archetype))
1094 .flat_map(|archetype| {
1095 filter.columns_iter().filter_map(|(type_hash, unique)| {
1096 Some((
1097 archetype.entities(),
1098 archetype.dynamic_column(type_hash, unique).ok()?,
1099 ))
1100 })
1101 })
1102 .collect(),
1103 }
1104 }
1105
1106 pub fn access(&self, entity: Entity) -> Option<DynamicQueryItem> {
1107 let columns = self
1108 .columns
1109 .iter()
1110 .map(|(type_hash, unique)| {
1111 self.access
1112 .iter()
1113 .find(|(map, access)| {
1114 map.contains(entity)
1115 && access.info().type_hash() == *type_hash
1116 && access.is_unique() == *unique
1117 })
1118 .and_then(|(map, access)| unsafe {
1119 std::mem::transmute(access.dynamic_item(map.index_of(entity).unwrap()).ok())
1120 })
1121 })
1122 .collect::<Option<Vec<_>>>()?;
1123 Some(DynamicQueryItem { entity, columns })
1124 }
1125}