intuicio_framework_ecs/
query.rs

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            // TODO: might be fucked up here.
317            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            // TODO: might be fucked up here.
337            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            // TODO: might be fucked up here.
368            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            // TODO: might be fucked up here.
392            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    /// [(column type, unique access)]
948    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    /// [(column type, unique access)]
1020    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    /// [(column type, unique access)]
1080    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}