Skip to main content

gizmo_core/
query.rs

1use crate::archetype::Archetype;
2use crate::world::World;
3use std::any::TypeId;
4use std::marker::PhantomData;
5
6// =========================================================================
7// FETCH COMPONENT TRAIT
8// =========================================================================
9
10pub trait FetchComponent {
11    type Component: 'static;
12    type Fetch<'w>: Copy; // Raw pointers are Copy
13    type Item<'w>;
14    type Slice<'w>;
15
16    const IS_MUT: bool;
17
18    /// Bir archetype bazında ham pointer fetch hazırlar.
19    ///
20    /// # Safety
21    /// Archetype geçerli olmalı ve döndürülen fetch pointer'ı archetype'ın yaşam süresi boyunca geçerli kalmalıdır.
22    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, system_tick: u32) -> Option<Self::Fetch<'w>>;
23
24    /// Ham pointer'dan veriyi getirir.
25    ///
26    /// # Safety
27    /// `row` değeri archetype'ın eleman sayısından küçük olmalıdır.
28    unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w>;
29
30    /// Chunk olarak ardışık belleği Slice şeklinde getirir (SIMD).
31    ///
32    /// # Safety
33    /// `len` değeri archetype'ın eleman sayısını aşmamalıdır.
34    unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w>;
35}
36
37impl<T: crate::component::Component> FetchComponent for &T {
38    type Component = T;
39    type Fetch<'w> = (*const u8, Option<*const crate::archetype::sparse_set::ComponentSparseSet>);
40    type Item<'w> = &'w T;
41    type Slice<'w> = &'w [T];
42    const IS_MUT: bool = false;
43
44    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, _system_tick: u32) -> Option<Self::Fetch<'w>> {
45        if T::storage_type() == crate::component::StorageType::SparseSet {
46            let set = world.sparse_sets.get(&TypeId::of::<T>())?;
47            Some((std::ptr::null(), Some(set as *const _)))
48        } else {
49            let col = arch.get_column(TypeId::of::<T>())?;
50            Some((col.data_ptr(), None))
51        }
52    }
53
54    unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
55        if let Some(set_ptr) = fetch.1 {
56            let set = &*set_ptr;
57            let ptr = set.get_ptr(entity_id).unwrap() as *const T;
58            &*ptr
59        } else {
60            let ptr = fetch.0.add(row * std::mem::size_of::<T>()) as *const T;
61            &*ptr
62        }
63    }
64
65    unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
66        if fetch.1.is_some() {
67            panic!("Cannot use iter_chunks with SparseSet components");
68        }
69        std::slice::from_raw_parts(fetch.0 as *const T, len)
70    }
71}
72
73pub struct Mut<'a, T: 'static> {
74    value: &'a mut T,
75    ticks: &'a mut crate::archetype::ComponentTicks,
76    current_tick: u32,
77}
78
79impl<T> std::ops::Deref for Mut<'_, T> {
80    type Target = T;
81    #[inline]
82    fn deref(&self) -> &T {
83        self.value
84    }
85}
86
87impl<T> std::ops::DerefMut for Mut<'_, T> {
88    #[inline]
89    fn deref_mut(&mut self) -> &mut T {
90        self.ticks.changed = self.current_tick;
91        self.value
92    }
93}
94
95impl<'a, T> Mut<'a, T> {
96    #[inline]
97    pub fn bypass_change_detection(&mut self) -> &mut T {
98        self.value
99    }
100}
101
102impl<T: crate::component::Component> FetchComponent for Mut<'_, T> {
103    type Component = T;
104    type Fetch<'w> = (*mut u8, *mut crate::archetype::ComponentTicks, u32, Option<*mut crate::archetype::sparse_set::ComponentSparseSet>);
105    type Item<'w> = Mut<'w, T>;
106    type Slice<'w> = &'w mut [T];
107    const IS_MUT: bool = true;
108
109    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, system_tick: u32) -> Option<Self::Fetch<'w>> {
110        if T::storage_type() == crate::component::StorageType::SparseSet {
111            let world_mut = world as *const World as *mut World;
112            let set = (*world_mut).sparse_sets.get_mut(&TypeId::of::<T>())?;
113            Some((std::ptr::null_mut(), std::ptr::null_mut(), system_tick, Some(set as *mut _)))
114        } else {
115            let col = arch.get_column_mut(TypeId::of::<T>())?;
116            Some((col.data_ptr_mut(), col.ticks_ptr_mut(), system_tick, None))
117        }
118    }
119
120    unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
121        let (data_ptr, ticks_ptr, system_tick, set_opt) = fetch;
122        if let Some(set_ptr) = set_opt {
123            let set = &mut *set_ptr;
124            // Get index of entity in dense array to access ticks
125            let e = entity_id as usize;
126            let dense_row = set.sparse[e] as usize;
127            let ptr = set.dense.get_unchecked_mut(dense_row) as *mut T;
128            Mut {
129                value: &mut *ptr,
130                ticks: &mut set.ticks[dense_row],
131                current_tick: system_tick,
132            }
133        } else {
134            let ptr = data_ptr.add(row * std::mem::size_of::<T>()) as *mut T;
135            Mut {
136                value: &mut *ptr,
137                ticks: &mut *ticks_ptr.add(row),
138                current_tick: system_tick,
139            }
140        }
141    }
142
143    unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
144        let (data_ptr, ticks_ptr, system_tick, set_opt) = fetch;
145        if set_opt.is_some() {
146            panic!("Cannot use iter_chunks with SparseSet components");
147        }
148        let ticks = std::slice::from_raw_parts_mut(ticks_ptr, len);
149        for tick in ticks.iter_mut() {
150            tick.changed = system_tick;
151        }
152        std::slice::from_raw_parts_mut(data_ptr as *mut T, len)
153    }
154}
155
156// =========================================================================
157// WORLD QUERY TRAIT
158// =========================================================================
159
160pub trait WorldQuery {
161    type StaticType: 'static;
162    type Fetch<'w>: Copy;
163    type Item<'w>;
164    type Slice<'w>;
165
166    /// # Safety
167    /// Archetype geçerli olmalı ve döndürülen fetch pointer'ı archetype'ın yaşam süresi boyunca geçerli kalmalıdır.
168    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, system_tick: u32) -> Option<Self::Fetch<'w>>;
169    fn check_aliasing(types: &mut Vec<(TypeId, bool)>);
170    fn matches_archetype(arch: &Archetype) -> bool;
171
172    /// # Safety
173    /// `row` değeri archetype'ın eleman sayısından küçük olmalıdır.
174    unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w>;
175
176    /// # Safety
177    /// Geçerli bir fetch ve archetype sınırları içinde bir `row` sağlanmalıdır.
178    unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, system_tick: u32) -> bool;
179
180    /// # Safety
181    /// `len` değeri archetype'ın eleman sayısını aşmamalıdır.
182    unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w>;
183}
184
185// =========================================================================
186// QUERY STRUCT
187// =========================================================================
188
189pub struct Query<'w, Q: WorldQuery + ?Sized> {
190    world: &'w World,
191    matching_archetypes: Vec<usize>,
192    _marker: PhantomData<Q>,
193}
194
195impl<'w, Q: WorldQuery> Query<'w, Q> {
196    pub fn new(world: &'w World) -> Option<Self> {
197        let mut used_types = Vec::new();
198        Q::check_aliasing(&mut used_types);
199        let matching = world
200            .archetype_index
201            .matching_archetypes_readonly(Q::matches_archetype);
202        Some(Self {
203            world,
204            matching_archetypes: matching,
205            _marker: PhantomData,
206        })
207    }
208
209    pub fn new_cached(world: &'w mut World) -> Option<Self> {
210        let mut used_types = Vec::new();
211        Q::check_aliasing(&mut used_types);
212        let matching = world
213            .archetype_index
214            .matching_archetypes(TypeId::of::<Q::StaticType>(), Q::matches_archetype)
215            .to_vec();
216        Some(Self {
217            world,
218            matching_archetypes: matching,
219            _marker: PhantomData,
220        })
221    }
222
223    pub fn iter<'a>(&'a self) -> QueryIter<'a, 'w, Q> {
224        QueryIter {
225            world: self.world,
226            archetype_indices: &self.matching_archetypes,
227            current_arch_idx: 0,
228            current_row: 0,
229            current_fetch: None,
230            _marker: PhantomData,
231            _marker_w: PhantomData,
232        }
233    }
234
235    pub fn iter_mut<'a>(&'a mut self) -> QueryIter<'a, 'w, Q> {
236        self.iter()
237    }
238
239    pub fn iter_chunks<'a>(&'a self) -> QueryChunksIter<'a, 'w, Q> {
240        QueryChunksIter {
241            world: self.world,
242            archetype_indices: &self.matching_archetypes,
243            current_arch_idx: 0,
244            _marker: PhantomData,
245        }
246    }
247
248    pub fn iter_chunks_mut<'a>(&'a mut self) -> QueryChunksIter<'a, 'w, Q> {
249        self.iter_chunks()
250    }
251
252    #[inline]
253    pub fn get(&self, entity_id: u32) -> Option<Q::Item<'_>> {
254        let loc = self.world.entity_location(entity_id);
255        if !loc.is_valid() {
256            return None;
257        }
258        let arch = &self.world.archetype_index.archetypes[loc.archetype_id as usize];
259        unsafe {
260            let fetch = Q::fetch_raw(self.world, arch, self.world.tick)?;
261            if !Q::filter_row(fetch, loc.row as usize, entity_id, self.world.tick) {
262                return None;
263            }
264            Some(Q::get_item(fetch, loc.row as usize, entity_id))
265        }
266    }
267
268    #[inline]
269    pub fn get_mut(&self, entity_id: u32) -> Option<Q::Item<'_>> {
270        self.get(entity_id)
271    }
272
273    #[inline]
274    pub fn entity_count(&self) -> usize {
275        self.matching_archetypes
276            .iter()
277            .map(|&idx| self.world.archetype_index.archetypes[idx].len())
278            .sum()
279    }
280
281    #[inline]
282    pub fn len(&self) -> usize {
283        self.entity_count()
284    }
285
286    #[inline]
287    pub fn is_empty(&self) -> bool {
288        self.entity_count() == 0
289    }
290
291    /// Belirli bir entity'nin bu query'ye ait olup olmadığını kontrol eder.
292    #[inline]
293    pub fn contains(&self, entity_id: u32) -> bool {
294        self.get(entity_id).is_some()
295    }
296
297    pub fn entities<'a>(&'a self) -> impl Iterator<Item = u32> + 'a {
298        self.iter().map(|(id, _)| id)
299    }
300
301    /// İş parçacığı havuzu (Work-Stealing) ile çalışan lock-free paralel iterasyon
302    pub fn par_for_each<F>(&self, func: F)
303    where
304        F: Fn((u32, Q::Item<'_>)) + Send + Sync,
305    {
306        use rayon::prelude::*;
307
308        // Pointer taşıyıcı wrapper — Güvenlidir çünkü Query::new() check_aliasing yapmıştır
309        #[derive(Copy, Clone)]
310        struct FetchWrapper<T>(T);
311        unsafe impl<T> Send for FetchWrapper<T> {}
312        unsafe impl<T> Sync for FetchWrapper<T> {}
313
314        impl<T: Copy> FetchWrapper<T> {
315            fn get(&self) -> T {
316                self.0
317            }
318        }
319
320        let tick = self.world.tick;
321        self.matching_archetypes.par_iter().for_each(|&arch_idx| {
322            let arch = &self.world.archetype_index.archetypes[arch_idx];
323            if let Some(fetch) = unsafe { Q::fetch_raw(self.world, arch, tick) } {
324                let len = arch.len();
325                let wrapped_fetch = FetchWrapper(fetch);
326                let entities_ptr = FetchWrapper(arch.entities().as_ptr());
327                let func_ref = &func;
328
329                // Her Archetype'ı cache dostu chunk'lar halinde ayırıp process ediyoruz
330                // Chunk size: 512 (Bevy benzeri)
331                (0..len)
332                    .into_par_iter()
333                    .with_min_len(512)
334                    .for_each(move |row| unsafe {
335                        let id = *entities_ptr.get().add(row);
336                        if Q::filter_row(wrapped_fetch.get(), row, id, tick) {
337                            let item = Q::get_item(wrapped_fetch.get(), row, id);
338                            func_ref((id, item));
339                        }
340                    });
341            }
342        });
343    }
344
345    pub fn par_for_each_mut<F>(&mut self, func: F)
346    where
347        F: Fn((u32, Q::Item<'_>)) + Send + Sync,
348    {
349        self.par_for_each(func);
350    }
351}
352
353// =========================================================================
354// QUERY ITERATOR
355// =========================================================================
356
357pub struct QueryIter<'a, 'w, Q: WorldQuery> {
358    world: &'a World,
359    archetype_indices: &'a [usize],
360    current_arch_idx: usize,
361    current_row: usize,
362    current_fetch: Option<Q::Fetch<'a>>,
363    _marker: PhantomData<Q>,
364    _marker_w: PhantomData<&'w ()>,
365}
366
367impl<'a, 'w, Q: WorldQuery> Iterator for QueryIter<'a, 'w, Q>
368where
369    'w: 'a,
370{
371    type Item = (u32, Q::Item<'a>);
372
373    fn next(&mut self) -> Option<Self::Item> {
374        loop {
375            if self.current_arch_idx >= self.archetype_indices.len() {
376                return None;
377            }
378
379            let arch_idx = self.archetype_indices[self.current_arch_idx];
380            let arch = &self.world.archetype_index.archetypes[arch_idx];
381
382            let fetch = match self.current_fetch {
383                Some(f) => f,
384                None => {
385                    match unsafe { Q::fetch_raw(self.world, arch, self.world.tick) } {
386                        Some(f) => {
387                            self.current_fetch = Some(f);
388                            self.current_row = 0;
389                            f
390                        }
391                        None => {
392                            // Bu archetype bu query'ye uymuyor, sonrakine geç
393                            self.current_arch_idx += 1;
394                            continue;
395                        }
396                    }
397                }
398            };
399
400            if self.current_row < arch.len() {
401                let row = self.current_row;
402                self.current_row += 1;
403                let id = arch.entities()[row];
404                if unsafe { Q::filter_row(fetch, row, id, self.world.tick) } {
405                    let item = unsafe { Q::get_item(fetch, row, id) };
406                    return Some((id, item));
407                }
408                continue;
409            }
410
411            self.current_fetch = None;
412            self.current_arch_idx += 1;
413        }
414    }
415
416    #[inline(always)]
417    fn for_each<F>(self, mut f: F)
418    where
419        Self: Sized,
420        F: FnMut(Self::Item),
421    {
422        for &arch_idx in self.archetype_indices {
423            let arch = &self.world.archetype_index.archetypes[arch_idx];
424            let len = arch.len();
425            if len == 0 {
426                continue;
427            }
428            if let Some(fetch) = unsafe { Q::fetch_raw(self.world, arch, self.world.tick) } {
429                let entities = arch.entities();
430                for row in 0..len {
431                    let id = entities[row];
432                    if unsafe { Q::filter_row(fetch, row, id, self.world.tick) } {
433                        let item = unsafe { Q::get_item(fetch, row, id) };
434                        f((id, item));
435                    }
436                }
437            }
438        }
439    }
440}
441
442// =========================================================================
443// QUERY CHUNKS ITERATOR
444// =========================================================================
445
446pub struct QueryChunksIter<'a, 'w, Q: WorldQuery> {
447    world: &'a World,
448    archetype_indices: &'a [usize],
449    current_arch_idx: usize,
450    _marker: PhantomData<&'w Q>,
451}
452
453impl<'a, 'w, Q: WorldQuery> Iterator for QueryChunksIter<'a, 'w, Q>
454where
455    'w: 'a,
456{
457    type Item = (&'a [u32], Q::Slice<'a>);
458
459    fn next(&mut self) -> Option<Self::Item> {
460        while self.current_arch_idx < self.archetype_indices.len() {
461            let arch_idx = self.archetype_indices[self.current_arch_idx];
462            self.current_arch_idx += 1;
463
464            let arch = &self.world.archetype_index.archetypes[arch_idx];
465            let len = arch.len();
466            if len == 0 {
467                continue;
468            }
469
470            let fetch = match unsafe { Q::fetch_raw(self.world, arch, self.world.tick) } {
471                Some(f) => f,
472                None => continue,
473            };
474
475            let ids = unsafe { std::slice::from_raw_parts(arch.entities().as_ptr(), len) };
476            let slice = unsafe { Q::get_slice(fetch, len) };
477
478            return Some((ids, slice));
479        }
480        None
481    }
482}
483
484// =========================================================================
485// ALIASING & IMPLS
486// =========================================================================
487
488/// Mutable aliasing kontrolü — aynı `TypeId`'ye iki mutable erişim varsa **UB** olur.
489///
490/// # Invariant
491/// Bir query içinde aynı component tipine birden fazla mutable erişim (`Mut<T>`)
492/// **kesinlikle yasaktır**. `Query<(Mut<Position>, Mut<Position>)>` gibi bir kullanım
493/// çalışma zamanında panic atar. Bu kontrol compile-time'da yapılamaz çünkü Rust'ın
494/// tip sistemi `TypeId` eşitliğini const-context'te karşılaştıramaz.
495///
496/// # Güvenli Kullanım
497/// - `Query<(&Position, Mut<Velocity>)>` → ✅ (farklı tipler)
498/// - `Query<(Mut<Position>, Mut<Velocity>)>` → ✅ (farklı tipler)
499/// - `Query<(Mut<Position>, Mut<Position>)>` → ❌ PANIC!
500/// - `Query<(&Position, &Position)>` → ✅ (ikisi de immutable — aliasing güvenli)
501#[inline]
502fn check(tid: TypeId, is_mut: bool, types: &mut Vec<(TypeId, bool)>) {
503    for &(existing_tid, existing_mut) in types.iter() {
504        if existing_tid == tid && (existing_mut || is_mut) {
505            panic!(
506                "Query aliasing UB detected! Component TypeId {:?} is accessed mutably more than once \
507                 in the same query. This would cause undefined behavior. \
508                 Use separate queries for components of the same type that need independent mutable access.",
509                tid
510            );
511        }
512    }
513    types.push((tid, is_mut));
514}
515
516impl<T0: FetchComponent> WorldQuery for T0 where T0::Component: crate::component::Component {
517    type StaticType = T0::Component;
518    type Fetch<'w> = T0::Fetch<'w>;
519    type Item<'w> = T0::Item<'w>;
520    type Slice<'w> = T0::Slice<'w>;
521
522    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
523        T0::fetch_raw(world, arch, tick)
524    }
525    fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
526        check(TypeId::of::<T0::Component>(), T0::IS_MUT, types);
527    }
528    fn matches_archetype(arch: &Archetype) -> bool {
529        if <T0::Component as crate::component::Component>::storage_type() == crate::component::StorageType::SparseSet { true } else { arch.has_component(TypeId::of::<T0::Component>()) }
530    }
531
532    unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
533        T0::get_item(fetch, row, entity_id)
534    }
535
536    unsafe fn filter_row<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32, _tick: u32) -> bool {
537        true
538    }
539
540    unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
541        T0::get_slice(fetch, len)
542    }
543}
544
545pub struct Changed<T>(PhantomData<T>);
546
547impl<T: crate::component::Component> WorldQuery for Changed<T> {
548    type StaticType = Changed<T>;
549    type Fetch<'w> = *const crate::archetype::ComponentTicks;
550    type Item<'w> = ();
551    type Slice<'w> = ();
552
553    unsafe fn fetch_raw<'w>(_world: &'w World, arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
554        let col = arch.get_column(TypeId::of::<T>())?;
555        Some(col.ticks_ptr())
556    }
557
558    fn check_aliasing(_types: &mut Vec<(TypeId, bool)>) {}
559
560    fn matches_archetype(arch: &Archetype) -> bool {
561        if T::storage_type() == crate::component::StorageType::SparseSet { true } else { arch.has_component(TypeId::of::<T>()) }
562    }
563
564    unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, _entity_id: u32, tick: u32) -> bool {
565        if T::storage_type() == crate::component::StorageType::SparseSet {
566            // TODO: Proper sparse set changed tracking, for now true if exists
567            true 
568        } else {
569            let ticks = &*fetch.add(row);
570            ticks.changed == tick
571        }
572    }
573
574    unsafe fn get_item<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32) -> Self::Item<'w> {}
575
576    unsafe fn get_slice<'w>(_fetch: Self::Fetch<'w>, _len: usize) -> Self::Slice<'w> {}
577}
578
579pub struct Added<T>(PhantomData<T>);
580
581impl<T: crate::component::Component> WorldQuery for Added<T> {
582    type StaticType = Added<T>;
583    type Fetch<'w> = *const crate::archetype::ComponentTicks;
584    type Item<'w> = ();
585    type Slice<'w> = ();
586
587    unsafe fn fetch_raw<'w>(_world: &'w World, arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
588        let col = arch.get_column(TypeId::of::<T>())?;
589        Some(col.ticks_ptr())
590    }
591
592    fn check_aliasing(_types: &mut Vec<(TypeId, bool)>) {}
593
594    fn matches_archetype(arch: &Archetype) -> bool {
595        if T::storage_type() == crate::component::StorageType::SparseSet { true } else { arch.has_component(TypeId::of::<T>()) }
596    }
597
598    unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, _entity_id: u32, tick: u32) -> bool {
599        if T::storage_type() == crate::component::StorageType::SparseSet {
600            true 
601        } else {
602            let ticks = &*fetch.add(row);
603            ticks.added == tick
604        }
605    }
606
607    unsafe fn get_item<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32) -> Self::Item<'w> {}
608
609    unsafe fn get_slice<'w>(_fetch: Self::Fetch<'w>, _len: usize) -> Self::Slice<'w> {}
610}
611
612macro_rules! impl_query_tuple {
613    ($($t:ident),*) => {
614        #[allow(non_snake_case)]
615        impl<$($t: WorldQuery),*> WorldQuery for ($($t,)*) {
616            type StaticType = ($($t::StaticType,)*);
617            type Fetch<'w> = ($($t::Fetch<'w>,)*);
618            type Item<'w> = ($($t::Item<'w>,)*);
619            type Slice<'w> = ($($t::Slice<'w>,)*);
620
621            unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
622                Some(($($t::fetch_raw(world, arch, tick)?,)*))
623            }
624            fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
625                $($t::check_aliasing(types);)*
626            }
627            fn matches_archetype(arch: &Archetype) -> bool {
628                $($t::matches_archetype(arch) &&)* true
629            }
630            unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
631                let ($($t,)*) = fetch;
632                ($($t::get_item($t, row, entity_id),)*)
633            }
634            unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, tick: u32) -> bool {
635                let ($($t,)*) = fetch;
636                $($t::filter_row($t, row, entity_id, tick) &&)* true
637            }
638            unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
639                let ($($t,)*) = fetch;
640                ($($t::get_slice($t, len),)*)
641            }
642        }
643    };
644}
645
646impl_query_tuple!(T0, T1);
647impl_query_tuple!(T0, T1, T2);
648impl_query_tuple!(T0, T1, T2, T3);
649impl_query_tuple!(T0, T1, T2, T3, T4);
650impl_query_tuple!(T0, T1, T2, T3, T4, T5);
651impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6);
652impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
653impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
654impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
655impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
656impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
657
658// =========================================================================
659// ADVANCED QUERY FILTERS
660// =========================================================================
661
662pub struct With<T>(PhantomData<T>);
663
664impl<T: crate::component::Component> WorldQuery for With<T> {
665    type StaticType = With<T>;
666    type Fetch<'w> = ();
667    type Item<'w> = ();
668    type Slice<'w> = ();
669
670    unsafe fn fetch_raw<'w>(_world: &'w World, _arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
671        Some(())
672    }
673
674    fn check_aliasing(_types: &mut Vec<(TypeId, bool)>) {}
675
676    fn matches_archetype(arch: &Archetype) -> bool {
677        if T::storage_type() == crate::component::StorageType::SparseSet { true } else { arch.has_component(TypeId::of::<T>()) }
678    }
679
680    unsafe fn filter_row<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32, _tick: u32) -> bool {
681        true
682    }
683    unsafe fn get_item<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32) -> Self::Item<'w> {}
684    unsafe fn get_slice<'w>(_fetch: Self::Fetch<'w>, _len: usize) -> Self::Slice<'w> {}
685}
686
687pub struct Without<T>(PhantomData<T>);
688
689impl<T: crate::component::Component> WorldQuery for Without<T> {
690    type StaticType = Without<T>;
691    type Fetch<'w> = ();
692    type Item<'w> = ();
693    type Slice<'w> = ();
694
695    unsafe fn fetch_raw<'w>(_world: &'w World, _arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
696        Some(())
697    }
698
699    fn check_aliasing(_types: &mut Vec<(TypeId, bool)>) {}
700
701    fn matches_archetype(arch: &Archetype) -> bool {
702        if T::storage_type() == crate::component::StorageType::SparseSet { true } else { !arch.has_component(TypeId::of::<T>()) }
703    }
704
705    unsafe fn filter_row<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32, _tick: u32) -> bool {
706        true
707    }
708    unsafe fn get_item<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32) -> Self::Item<'w> {}
709    unsafe fn get_slice<'w>(_fetch: Self::Fetch<'w>, _len: usize) -> Self::Slice<'w> {}
710}
711
712pub struct Or<T1, T2>(PhantomData<(T1, T2)>);
713
714impl<T1: WorldQuery, T2: WorldQuery> WorldQuery for Or<T1, T2> {
715    type StaticType = Or<T1::StaticType, T2::StaticType>;
716    type Fetch<'w> = ();
717    type Item<'w> = ();
718    type Slice<'w> = ();
719
720    unsafe fn fetch_raw<'w>(_world: &'w World, _arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
721        Some(())
722    }
723
724    fn check_aliasing(_types: &mut Vec<(TypeId, bool)>) {}
725
726    fn matches_archetype(arch: &Archetype) -> bool {
727        T1::matches_archetype(arch) || T2::matches_archetype(arch)
728    }
729
730    unsafe fn filter_row<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32, _tick: u32) -> bool {
731        true
732    }
733    unsafe fn get_item<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32) -> Self::Item<'w> {}
734    unsafe fn get_slice<'w>(_fetch: Self::Fetch<'w>, _len: usize) -> Self::Slice<'w> {}
735}
736
737#[cfg(test)]
738mod tests {
739    use super::*;
740    use crate::impl_component;
741
742    #[derive(Debug, Clone, PartialEq)]
743    struct Position {
744        x: f32,
745        y: f32,
746    }
747    impl_component!(Position);
748
749    #[derive(Debug, Clone, PartialEq)]
750    struct Velocity {
751        x: f32,
752        y: f32,
753    }
754    impl_component!(Velocity);
755
756    /// `Query<(Mut<Position>, Mut<Position>)>` gibi aynı tipe çift mutable erişim
757    /// denemesi panic ile engellenmeli.
758    #[test]
759    #[should_panic(expected = "Query aliasing UB detected")]
760    fn test_same_type_mut_mut_panics() {
761        let mut types = Vec::new();
762        // İlk Mut<Position> — sorunsuz eklenir
763        check(TypeId::of::<Position>(), true, &mut types);
764        // İkinci Mut<Position> — PANIC olmalı!
765        check(TypeId::of::<Position>(), true, &mut types);
766    }
767
768    /// `Query<(&Position, Mut<Position>)>` — bir immutable, bir mutable aynı tipe erişim:
769    /// Bu da panic olmalı çünkü &T + &mut T alias oluşturur.
770    #[test]
771    #[should_panic(expected = "Query aliasing UB detected")]
772    fn test_same_type_ref_mut_panics() {
773        let mut types = Vec::new();
774        check(TypeId::of::<Position>(), false, &mut types); // &Position
775        check(TypeId::of::<Position>(), true, &mut types); // Mut<Position> — PANIC!
776    }
777
778    /// `Query<(Mut<Position>, Mut<Velocity>)>` — farklı tipler, sorunsuz çalışmalı.
779    #[test]
780    fn test_different_types_mut_mut_ok() {
781        let mut types = Vec::new();
782        check(TypeId::of::<Position>(), true, &mut types);
783        check(TypeId::of::<Velocity>(), true, &mut types);
784        assert_eq!(types.len(), 2);
785    }
786
787    /// `Query<(&Position, &Position)>` — aynı tipe çift immutable erişim güvenlidir.
788    #[test]
789    fn test_same_type_ref_ref_ok() {
790        let mut types = Vec::new();
791        check(TypeId::of::<Position>(), false, &mut types);
792        check(TypeId::of::<Position>(), false, &mut types);
793        assert_eq!(types.len(), 2);
794    }
795
796    /// World üzerinden Query oluşturulduğunda aliasing kontrolünün çalıştığını doğrular.
797    #[test]
798    fn test_query_new_with_valid_types() {
799        let mut world = crate::World::new();
800        world.register_component_type::<Position>();
801        world.register_component_type::<Velocity>();
802        let e = world.spawn();
803        world.add_component(e, Position { x: 1.0, y: 2.0 });
804        world.add_component(e, Velocity { x: 0.0, y: 0.0 });
805
806        // Farklı tipler — Query oluşturulabilmeli
807        let q = world.query::<(Mut<Position>, Mut<Velocity>)>();
808        assert!(q.is_some());
809    }
810}