1use core::fmt::Display;
2
3use crate::{
4 component::{ComponentId, Components},
5 query::{Access, QueryData},
6};
7
8#[inline(never)]
10pub fn has_conflicts<Q: QueryData>(components: &Components) -> Result<(), QueryAccessError> {
11 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 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#[derive(Copy, Clone, Debug, PartialEq)]
45pub enum EcsAccessType<'a> {
46 Component(EcsAccessLevel),
48 Resource(ResourceAccessLevel),
50 Access(&'a Access),
52 Empty,
54}
55
56impl<'a> EcsAccessType<'a> {
57 #[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 | (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 (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#[derive(Clone, Copy, Debug, PartialEq)]
156pub enum EcsAccessLevel {
157 Read(ComponentId),
159 Write(ComponentId),
161 ReadAll,
163 WriteAll,
165}
166
167#[derive(Copy, Clone, Debug, PartialEq)]
169pub enum ResourceAccessLevel {
170 Read(ComponentId),
172 Write(ComponentId),
174}
175
176pub 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 (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(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 (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#[derive(Clone, Copy, Debug, PartialEq)]
265pub enum QueryAccessError {
266 ComponentNotRegistered,
268 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 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 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 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 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}