edict/relation/query/
relates_to.rs

1use core::{any::TypeId, marker::PhantomData, ptr::NonNull};
2
3use crate::{
4    archetype::Archetype,
5    component::ComponentInfo,
6    entity::EntityId,
7    epoch::EpochId,
8    query::{AsQuery, Fetch, ImmutableQuery, IntoQuery, Query, Read, SendQuery, Write, WriteAlias},
9    relation::{OriginComponent, Relation},
10    type_id, Access,
11};
12
13/// Query for origins of relation with specified target.
14///
15/// Yields relation instance.
16pub struct RelatesTo<R> {
17    target: EntityId,
18    phantom: PhantomData<R>,
19}
20
21impl_debug!(RelatesTo<R> { target });
22impl_copy!(RelatesTo<R>);
23
24impl<R> RelatesTo<R> {
25    /// Returns relation query bound to one specific target entity.
26    pub fn new(target: EntityId) -> Self {
27        RelatesTo {
28            target,
29            phantom: PhantomData,
30        }
31    }
32}
33
34/// Fetch for the [`RelatesTo<R>`] query.
35pub struct FetchRelatesToRead<'a, R: Relation> {
36    target: EntityId,
37    item_idx: usize,
38    ptr: NonNull<OriginComponent<R>>,
39    marker: PhantomData<&'a OriginComponent<R>>,
40}
41
42unsafe impl<'a, R> Fetch<'a> for FetchRelatesToRead<'a, R>
43where
44    R: Relation,
45{
46    type Item = &'a R;
47
48    #[inline(always)]
49    fn dangling() -> Self {
50        FetchRelatesToRead {
51            target: EntityId::dangling(),
52            ptr: NonNull::dangling(),
53            item_idx: 0,
54            marker: PhantomData,
55        }
56    }
57
58    #[inline(always)]
59    unsafe fn visit_item(&mut self, idx: u32) -> bool {
60        let origin_component = unsafe { &*self.ptr.as_ptr().add(idx as usize) };
61        let item_idx = origin_component
62            .relations()
63            .iter()
64            .position(|origin| origin.target == self.target);
65
66        match item_idx {
67            None => false,
68            Some(item_idx) => {
69                self.item_idx = item_idx;
70                true
71            }
72        }
73    }
74
75    #[inline(always)]
76    unsafe fn get_item(&mut self, idx: u32) -> &'a R {
77        let origin_component = unsafe { &*self.ptr.as_ptr().add(idx as usize) };
78        &origin_component.relations()[self.item_idx].relation
79    }
80}
81
82impl<R> AsQuery for RelatesTo<&R>
83where
84    R: Relation + 'static,
85{
86    type Query = RelatesTo<Read<R>>;
87}
88
89impl<R> AsQuery for RelatesTo<Read<R>>
90where
91    R: Relation + 'static,
92{
93    type Query = Self;
94}
95
96impl<R> IntoQuery for RelatesTo<Read<R>>
97where
98    R: Relation + 'static,
99{
100    #[inline(always)]
101    fn into_query(self) -> Self {
102        self
103    }
104}
105
106unsafe impl<R> Query for RelatesTo<Read<R>>
107where
108    R: Relation,
109{
110    type Item<'a> = &'a R;
111    type Fetch<'a> = FetchRelatesToRead<'a, R>;
112
113    const MUTABLE: bool = false;
114    const FILTERS_ENTITIES: bool = true;
115
116    #[inline(always)]
117    fn component_access(&self, comp: &ComponentInfo) -> Result<Option<Access>, WriteAlias> {
118        if comp.id() == type_id::<OriginComponent<R>>() {
119            Ok(Some(Access::Read))
120        } else {
121            Ok(None)
122        }
123    }
124
125    fn visit_archetype(&self, archetype: &Archetype) -> bool {
126        archetype.has_component(type_id::<OriginComponent<R>>())
127    }
128
129    #[inline(always)]
130    unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) {
131        f(type_id::<OriginComponent<R>>(), Access::Read)
132    }
133
134    #[inline(always)]
135    unsafe fn fetch<'a>(
136        &self,
137        _arch_idx: u32,
138        archetype: &'a Archetype,
139        _epoch: EpochId,
140    ) -> FetchRelatesToRead<'a, R> {
141        let component = unsafe {
142            archetype
143                .component(type_id::<OriginComponent<R>>())
144                .unwrap_unchecked()
145        };
146        debug_assert_eq!(component.id(), type_id::<OriginComponent<R>>());
147
148        let data = unsafe { component.data() };
149
150        FetchRelatesToRead {
151            target: self.target,
152            ptr: data.ptr.cast(),
153            item_idx: 0,
154            marker: PhantomData,
155        }
156    }
157}
158
159unsafe impl<R> ImmutableQuery for RelatesTo<Read<R>> where R: Relation {}
160unsafe impl<R> SendQuery for RelatesTo<Read<R>> where R: Relation + Sync {}
161
162/// Fetch for the `RelatesTo<R>` query.
163pub struct FetchRelatesToWrite<'a, R: Relation> {
164    target: EntityId,
165    item_idx: usize,
166    epoch: EpochId,
167    ptr: NonNull<OriginComponent<R>>,
168    entity_epochs: NonNull<EpochId>,
169    chunk_epochs: NonNull<EpochId>,
170    marker: PhantomData<&'a mut OriginComponent<R>>,
171}
172
173unsafe impl<'a, R> Fetch<'a> for FetchRelatesToWrite<'a, R>
174where
175    R: Relation,
176{
177    type Item = &'a mut R;
178
179    #[inline(always)]
180    fn dangling() -> Self {
181        FetchRelatesToWrite {
182            item_idx: 0,
183            target: EntityId::dangling(),
184            epoch: EpochId::start(),
185            ptr: NonNull::dangling(),
186            entity_epochs: NonNull::dangling(),
187            chunk_epochs: NonNull::dangling(),
188            marker: PhantomData,
189        }
190    }
191
192    #[inline(always)]
193    unsafe fn touch_chunk(&mut self, chunk_idx: u32) {
194        let chunk_epoch = unsafe { &mut *self.chunk_epochs.as_ptr().add(chunk_idx as usize) };
195        chunk_epoch.bump(self.epoch);
196    }
197
198    #[inline(always)]
199    unsafe fn visit_item(&mut self, idx: u32) -> bool {
200        let origin_component = unsafe { &*self.ptr.as_ptr().add(idx as usize) };
201        let item_idx = origin_component
202            .relations()
203            .iter()
204            .position(|origin| origin.target == self.target);
205
206        match item_idx {
207            None => false,
208            Some(item_idx) => {
209                self.item_idx = item_idx;
210                true
211            }
212        }
213    }
214
215    #[inline(always)]
216    unsafe fn get_item(&mut self, idx: u32) -> &'a mut R {
217        let entity_epoch = unsafe { &mut *self.entity_epochs.as_ptr().add(idx as usize) };
218        entity_epoch.bump(self.epoch);
219
220        let origin_component = unsafe { &mut *self.ptr.as_ptr().add(idx as usize) };
221        &mut origin_component.relations_mut()[self.item_idx].relation
222    }
223}
224
225impl<R> AsQuery for RelatesTo<&mut R>
226where
227    R: Relation,
228{
229    type Query = RelatesTo<Write<R>>;
230}
231
232impl<R> AsQuery for RelatesTo<Write<R>>
233where
234    R: Relation,
235{
236    type Query = Self;
237}
238
239impl<R> IntoQuery for RelatesTo<Write<R>>
240where
241    R: Relation,
242{
243    #[inline(always)]
244    fn into_query(self) -> Self {
245        self
246    }
247}
248
249unsafe impl<R> Query for RelatesTo<Write<R>>
250where
251    R: Relation,
252{
253    type Item<'a> = &'a mut R;
254    type Fetch<'a> = FetchRelatesToWrite<'a, R>;
255
256    const MUTABLE: bool = true;
257    const FILTERS_ENTITIES: bool = true;
258
259    #[inline(always)]
260    fn component_access(&self, comp: &ComponentInfo) -> Result<Option<Access>, WriteAlias> {
261        if comp.id() == type_id::<OriginComponent<R>>() {
262            Ok(Some(Access::Write))
263        } else {
264            Ok(None)
265        }
266    }
267
268    fn visit_archetype(&self, archetype: &Archetype) -> bool {
269        archetype.has_component(type_id::<OriginComponent<R>>())
270    }
271
272    #[inline(always)]
273    unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) {
274        f(type_id::<OriginComponent<R>>(), Access::Write)
275    }
276
277    #[inline(always)]
278    unsafe fn fetch<'a>(
279        &self,
280        _arch_idx: u32,
281        archetype: &'a Archetype,
282        epoch: EpochId,
283    ) -> FetchRelatesToWrite<'a, R> {
284        let component = unsafe {
285            archetype
286                .component(type_id::<OriginComponent<R>>())
287                .unwrap_unchecked()
288        };
289        debug_assert_eq!(component.id(), type_id::<OriginComponent<R>>());
290
291        let data = unsafe { component.data_mut() };
292        data.epoch.bump(epoch);
293
294        FetchRelatesToWrite {
295            target: self.target,
296            item_idx: 0,
297            epoch,
298            ptr: data.ptr.cast(),
299            entity_epochs: unsafe { NonNull::new_unchecked(data.entity_epochs.as_mut_ptr()) },
300            chunk_epochs: unsafe { NonNull::new_unchecked(data.chunk_epochs.as_mut_ptr()) },
301            marker: PhantomData,
302        }
303    }
304}
305
306unsafe impl<R> SendQuery for RelatesTo<Write<R>> where R: Relation + Send {}