bevy_ecs/query/
access_iter.rs

1use core::fmt::Display;
2
3use crate::{
4    component::{ComponentId, Components},
5    query::{Access, QueryData},
6};
7
8/// Check if `Q` has any internal conflicts.
9#[inline(never)]
10pub fn has_conflicts<Q: QueryData>(components: &Components) -> Result<(), QueryAccessError> {
11    // increasing this too much may slow down smaller queries
12    const MAX_SIZE: usize = 16;
13    let Some(state) = Q::get_state(components) else {
14        return Err(QueryAccessError::ComponentNotRegistered);
15    };
16    let iter = Q::iter_access(&state).enumerate();
17    let size = iter.size_hint().1.unwrap_or(MAX_SIZE);
18
19    if size > MAX_SIZE {
20        for (i, access) in iter {
21            for access_other in Q::iter_access(&state).take(i) {
22                if let Err(err) = access.is_compatible(access_other) {
23                    panic!("{}", err);
24                }
25            }
26        }
27    } else {
28        // we can optimize small sizes by caching the iteration result in an array on the stack
29        let mut inner_access = [EcsAccessType::Empty; MAX_SIZE];
30        for (i, access) in iter {
31            for access_other in inner_access.iter().take(i) {
32                if let Err(err) = access.is_compatible(*access_other) {
33                    panic!("{}", err);
34                }
35            }
36            inner_access[i] = access;
37        }
38    }
39
40    Ok(())
41}
42
43/// The data storage type that is being accessed.
44#[derive(Copy, Clone, Debug, PartialEq)]
45pub enum EcsAccessType<'a> {
46    /// Accesses [`Component`](crate::prelude::Component) data
47    Component(EcsAccessLevel),
48    /// Accesses [`Resource`](crate::prelude::Resource) data
49    Resource(ResourceAccessLevel),
50    /// borrowed access from [`WorldQuery::State`](crate::query::WorldQuery)
51    Access(&'a Access),
52    /// Does not access any data that can conflict.
53    Empty,
54}
55
56impl<'a> EcsAccessType<'a> {
57    /// Returns `Ok(())` if `self` and `other` are compatible. Returns a [`AccessConflictError`] otherwise.
58    #[inline(never)]
59    pub fn is_compatible(&self, other: Self) -> Result<(), AccessConflictError<'_>> {
60        use EcsAccessLevel::*;
61        use EcsAccessType::*;
62
63        match (*self, other) {
64            (Component(ReadAll), Component(Write(_)))
65            | (Component(Write(_)), Component(ReadAll))
66            | (Component(_), Component(WriteAll))
67            | (Component(WriteAll), Component(_)) => Err(AccessConflictError(*self, other)),
68
69            (Empty, _)
70            | (_, Empty)
71            | (Component(_), Resource(_))
72            | (Resource(_), Component(_))
73            // read only access doesn't conflict
74            | (Component(Read(_)), Component(Read(_)))
75            | (Component(ReadAll), Component(Read(_)))
76            | (Component(Read(_)), Component(ReadAll))
77            | (Component(ReadAll), Component(ReadAll))
78            | (Resource(ResourceAccessLevel::Read(_)), Resource(ResourceAccessLevel::Read(_))) => {
79                Ok(())
80            }
81
82            (Component(Read(id)), Component(Write(id_other)))
83            | (Component(Write(id)), Component(Read(id_other)))
84            | (Component(Write(id)), Component(Write(id_other)))
85            | (
86                Resource(ResourceAccessLevel::Read(id)),
87                Resource(ResourceAccessLevel::Write(id_other)),
88            )
89            | (
90                Resource(ResourceAccessLevel::Write(id)),
91                Resource(ResourceAccessLevel::Read(id_other)),
92            )
93            | (
94                Resource(ResourceAccessLevel::Write(id)),
95                Resource(ResourceAccessLevel::Write(id_other)),
96            ) => if id == id_other {
97                Err(AccessConflictError(*self, other))
98            } else {
99                Ok(())
100            },
101
102            // Borrowed Access
103            (Component(Read(component_id)), Access(access))
104            | (Access(access), Component(Read(component_id))) => if access.has_component_write(component_id) {
105                Err(AccessConflictError(*self, other))
106            } else {
107                Ok(())
108            },
109
110            (Component(Write(component_id)), Access(access))
111            | (Access(access), Component(Write(component_id))) => if access.has_component_read(component_id) {
112                Err(AccessConflictError(*self, other))
113            } else {
114                Ok(())
115            },
116
117            (Component(ReadAll), Access(access))
118            | (Access(access), Component(ReadAll)) => if access.has_any_component_write() {
119                Err(AccessConflictError(*self, other))
120            } else {
121                Ok(())
122            },
123
124            (Component(WriteAll), Access(access))
125            | (Access(access), Component(WriteAll))=> if access.has_any_component_read() {
126                Err(AccessConflictError(*self, other))
127            } else {
128                Ok(())
129            },
130
131            (Resource(ResourceAccessLevel::Read(component_id)), Access(access))
132            | (Access(access), Resource(ResourceAccessLevel::Read(component_id))) => if access.has_resource_write(component_id) {
133                Err(AccessConflictError(*self, other))
134            } else {
135                Ok(())
136            },
137            (Resource(ResourceAccessLevel::Write(component_id)), Access(access))
138            | (Access(access), Resource(ResourceAccessLevel::Write(component_id))) => if access.has_resource_read(component_id) {
139                Err(AccessConflictError(*self, other))
140            } else {
141                Ok(())
142            },
143
144            (Access(access), Access(other_access)) => if access.is_compatible(other_access) {
145                Ok(())
146            } else {
147                Err(AccessConflictError(*self, other))
148            },
149        }
150    }
151}
152
153/// The way the data will be accessed and whether we take access on all the components on
154/// an entity or just one component.
155#[derive(Clone, Copy, Debug, PartialEq)]
156pub enum EcsAccessLevel {
157    /// Reads [`Component`](crate::prelude::Component) with [`ComponentId`]
158    Read(ComponentId),
159    /// Writes [`Component`](crate::prelude::Component) with [`ComponentId`]
160    Write(ComponentId),
161    /// Potentially reads all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)
162    ReadAll,
163    /// Potentially writes all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)
164    WriteAll,
165}
166
167/// Access level needed by [`QueryData`] fetch to the resource.
168#[derive(Copy, Clone, Debug, PartialEq)]
169pub enum ResourceAccessLevel {
170    /// Reads the resource with [`ComponentId`]
171    Read(ComponentId),
172    /// Writes the resource with [`ComponentId`]
173    Write(ComponentId),
174}
175
176/// Error returned from [`EcsAccessType::is_compatible`]
177pub struct AccessConflictError<'a>(EcsAccessType<'a>, EcsAccessType<'a>);
178
179impl Display for AccessConflictError<'_> {
180    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
181        use EcsAccessLevel::*;
182        use EcsAccessType::*;
183
184        let AccessConflictError(a, b) = self;
185        match (a, b) {
186            // ReadAll/WriteAll + Component conflicts
187            (Component(ReadAll), Component(Write(id)))
188            | (Component(Write(id)), Component(ReadAll)) => {
189                write!(
190                    f,
191                    "Component read all access conflicts with component {id:?} write."
192                )
193            }
194            (Component(WriteAll), Component(Write(id)))
195            | (Component(Write(id)), Component(WriteAll)) => {
196                write!(
197                    f,
198                    "Component write all access conflicts with component {id:?} write."
199                )
200            }
201            (Component(WriteAll), Component(Read(id)))
202            | (Component(Read(id)), Component(WriteAll)) => {
203                write!(
204                    f,
205                    "Component write all access conflicts with component {id:?} read."
206                )
207            }
208            (Component(WriteAll), Component(ReadAll))
209            | (Component(ReadAll), Component(WriteAll)) => {
210                write!(f, "Component write all conflicts with component read all.")
211            }
212            (Component(WriteAll), Component(WriteAll)) => {
213                write!(f, "Component write all conflicts with component write all.")
214            }
215
216            // Component + Component conflicts
217            (Component(Read(id)), Component(Write(id_other)))
218            | (Component(Write(id_other)), Component(Read(id))) => write!(
219                f,
220                "Component {id:?} read conflicts with component {id_other:?} write."
221            ),
222            (Component(Write(id)), Component(Write(id_other))) => write!(
223                f,
224                "Component {id:?} write conflicts with component {id_other:?} write."
225            ),
226
227            // Borrowed Access conflicts
228            (Access(_), Component(Read(id))) | (Component(Read(id)), Access(_)) => write!(
229                f,
230                "Access has a write that conflicts with component {id:?} read."
231            ),
232            (Access(_), Component(Write(id))) | (Component(Write(id)), Access(_)) => write!(
233                f,
234                "Access has a read that conflicts with component {id:?} write."
235            ),
236            (Access(_), Component(ReadAll)) | (Component(ReadAll), Access(_)) => write!(
237                f,
238                "Access has a write that conflicts with component read all"
239            ),
240            (Access(_), Component(WriteAll)) | (Component(WriteAll), Access(_)) => write!(
241                f,
242                "Access has a read that conflicts with component write all"
243            ),
244            (Access(_), Resource(ResourceAccessLevel::Read(id)))
245            | (Resource(ResourceAccessLevel::Read(id)), Access(_)) => write!(
246                f,
247                "Access has a write that conflicts with resource {id:?} read."
248            ),
249            (Access(_), Resource(ResourceAccessLevel::Write(id)))
250            | (Resource(ResourceAccessLevel::Write(id)), Access(_)) => write!(
251                f,
252                "Access has a read that conflicts with resource {id:?} write."
253            ),
254            (Access(_), Access(_)) => write!(f, "Access conflicts with other Access"),
255
256            _ => {
257                unreachable!("Other accesses should be compatible");
258            }
259        }
260    }
261}
262
263/// Error returned from [`has_conflicts`].
264#[derive(Clone, Copy, Debug, PartialEq)]
265pub enum QueryAccessError {
266    /// Component was not registered on world
267    ComponentNotRegistered,
268    /// Entity did not have the requested components
269    EntityDoesNotMatch,
270}
271
272impl core::error::Error for QueryAccessError {}
273
274impl Display for QueryAccessError {
275    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
276        match *self {
277            QueryAccessError::ComponentNotRegistered => {
278                write!(
279                    f,
280                    "At least one component in Q was not registered in world. 
281                    Consider calling `World::register_component`"
282                )
283            }
284            QueryAccessError::EntityDoesNotMatch => {
285                write!(f, "Entity does not match Q")
286            }
287        }
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use super::*;
294    use crate::{
295        prelude::Component,
296        world::{EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, World},
297    };
298
299    #[derive(Component)]
300    struct C1;
301
302    #[derive(Component)]
303    struct C2;
304
305    fn setup_world() -> World {
306        let world = World::new();
307        let mut world = world;
308        world.register_component::<C1>();
309        world.register_component::<C2>();
310        world
311    }
312
313    #[test]
314    fn simple_compatible() {
315        let world = setup_world();
316        let c = world.components();
317
318        // Compatible
319        assert!(has_conflicts::<&mut C1>(c).is_ok());
320        assert!(has_conflicts::<&C1>(c).is_ok());
321        assert!(has_conflicts::<(&C1, &C1)>(c).is_ok());
322    }
323
324    #[test]
325    #[should_panic(expected = "conflicts")]
326    fn conflict_component_read_conflicts_write() {
327        let _ = has_conflicts::<(&C1, &mut C1)>(setup_world().components());
328    }
329
330    #[test]
331    #[should_panic(expected = "conflicts")]
332    fn conflict_component_write_conflicts_read() {
333        let _ = has_conflicts::<(&mut C1, &C1)>(setup_world().components());
334    }
335
336    #[test]
337    #[should_panic(expected = "conflicts")]
338    fn conflict_component_write_conflicts_write() {
339        let _ = has_conflicts::<(&mut C1, &mut C1)>(setup_world().components());
340    }
341
342    #[test]
343    fn entity_ref_compatible() {
344        let world = setup_world();
345        let c = world.components();
346
347        // Compatible
348        assert!(has_conflicts::<(EntityRef, &C1)>(c).is_ok());
349        assert!(has_conflicts::<(&C1, EntityRef)>(c).is_ok());
350        assert!(has_conflicts::<(EntityRef, EntityRef)>(c).is_ok());
351    }
352
353    #[test]
354    #[should_panic(expected = "conflicts")]
355    fn entity_ref_conflicts_component_write() {
356        let _ = has_conflicts::<(EntityRef, &mut C1)>(setup_world().components());
357    }
358
359    #[test]
360    #[should_panic(expected = "conflicts")]
361    fn component_write_conflicts_entity_ref() {
362        let _ = has_conflicts::<(&mut C1, EntityRef)>(setup_world().components());
363    }
364
365    #[test]
366    #[should_panic(expected = "conflicts")]
367    fn entity_mut_conflicts_component_read() {
368        let _ = has_conflicts::<(EntityMut, &C1)>(setup_world().components());
369    }
370
371    #[test]
372    #[should_panic(expected = "conflicts")]
373    fn component_read_conflicts_entity_mut() {
374        let _ = has_conflicts::<(&C1, EntityMut)>(setup_world().components());
375    }
376
377    #[test]
378    #[should_panic(expected = "conflicts")]
379    fn entity_mut_conflicts_component_write() {
380        let _ = has_conflicts::<(EntityMut, &mut C1)>(setup_world().components());
381    }
382
383    #[test]
384    #[should_panic(expected = "conflicts")]
385    fn component_write_conflicts_entity_mut() {
386        let _ = has_conflicts::<(&mut C1, EntityMut)>(setup_world().components());
387    }
388
389    #[test]
390    #[should_panic(expected = "conflicts")]
391    fn entity_mut_conflicts_entity_ref() {
392        let _ = has_conflicts::<(EntityMut, EntityRef)>(setup_world().components());
393    }
394
395    #[test]
396    #[should_panic(expected = "conflicts")]
397    fn entity_ref_conflicts_entity_mut() {
398        let _ = has_conflicts::<(EntityRef, EntityMut)>(setup_world().components());
399    }
400
401    #[test]
402    fn entity_ref_except_compatible() {
403        let world = setup_world();
404        let c = world.components();
405
406        // Compatible
407        assert!(has_conflicts::<(EntityRefExcept<C1>, &mut C1)>(c).is_ok());
408        assert!(has_conflicts::<(&mut C1, EntityRefExcept<C1>)>(c).is_ok());
409        assert!(has_conflicts::<(&C2, EntityRefExcept<C1>)>(c).is_ok());
410        assert!(has_conflicts::<(&mut C1, EntityRefExcept<(C1, C2)>,)>(c).is_ok());
411        assert!(has_conflicts::<(EntityRefExcept<(C1, C2)>, &mut C1,)>(c).is_ok());
412        assert!(has_conflicts::<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>,)>(c).is_ok());
413        assert!(has_conflicts::<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2,)>(c).is_ok());
414        assert!(has_conflicts::<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2,)>(c).is_ok());
415    }
416
417    #[test]
418    #[should_panic(expected = "conflicts")]
419    fn entity_ref_except_conflicts_component_write() {
420        let _ = has_conflicts::<(EntityRefExcept<C1>, &mut C2)>(setup_world().components());
421    }
422
423    #[test]
424    #[should_panic(expected = "conflicts")]
425    fn component_write_conflicts_entity_ref_except() {
426        let _ = has_conflicts::<(&mut C2, EntityRefExcept<C1>)>(setup_world().components());
427    }
428
429    #[test]
430    fn entity_mut_except_compatible() {
431        let world = setup_world();
432        let c = world.components();
433
434        // Compatible
435        assert!(has_conflicts::<(EntityMutExcept<C1>, &mut C1)>(c).is_ok());
436        assert!(has_conflicts::<(&mut C1, EntityMutExcept<C1>)>(c).is_ok());
437        assert!(has_conflicts::<(&mut C1, EntityMutExcept<(C1, C2)>,)>(c).is_ok());
438        assert!(has_conflicts::<(EntityMutExcept<(C1, C2)>, &mut C1,)>(c).is_ok());
439        assert!(has_conflicts::<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>,)>(c).is_ok());
440        assert!(has_conflicts::<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2,)>(c).is_ok());
441        assert!(has_conflicts::<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2,)>(c).is_ok());
442    }
443
444    #[test]
445    #[should_panic(expected = "conflicts")]
446    fn entity_mut_except_conflicts_component_read() {
447        let _ = has_conflicts::<(EntityMutExcept<C1>, &C2)>(setup_world().components());
448    }
449
450    #[test]
451    #[should_panic(expected = "conflicts")]
452    fn component_read_conflicts_entity_mut_except() {
453        let _ = has_conflicts::<(&C2, EntityMutExcept<C1>)>(setup_world().components());
454    }
455
456    #[test]
457    #[should_panic(expected = "conflicts")]
458    fn entity_mut_except_conflicts_component_write() {
459        let _ = has_conflicts::<(EntityMutExcept<C1>, &mut C2)>(setup_world().components());
460    }
461
462    #[test]
463    #[should_panic(expected = "conflicts")]
464    fn component_write_conflicts_entity_mut_except() {
465        let _ = has_conflicts::<(&mut C2, EntityMutExcept<C1>)>(setup_world().components());
466    }
467}