Skip to content

Commit 3d44c26

Browse files
committed
convert to result
1 parent b29dfbf commit 3d44c26

File tree

4 files changed

+164
-102
lines changed

4 files changed

+164
-102
lines changed

crates/bevy_ecs/src/query/access_iter.rs

Lines changed: 143 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,31 @@ use crate::{
44
};
55

66
/// The data storage type that is being accessed.
7-
#[derive(Clone, Copy)]
7+
#[derive(Clone, Copy, Debug)]
88
pub enum EcsAccessType {
9-
/// Accesses [`Component`] data
9+
/// Accesses [`Component`](crate::prelude::Component) data
1010
Component(EcsAccessLevel),
11-
/// Accesses [`Resource`] data
11+
/// Accesses [`Resource`](crate::prelude::Resource) data
1212
Resource(ResourceAccessLevel),
1313
}
1414

1515
/// The way the data will be accessed and whether we take access on all the components on
1616
/// an entity or just one component.
17-
#[derive(Clone, Copy)]
17+
#[derive(Clone, Copy, Debug)]
1818
pub enum EcsAccessLevel {
19-
/// Reads [`Component`] with [`ComponentId`]
19+
/// Reads [`Component`](crate::prelude::Component) with [`ComponentId`]
2020
Read(ComponentId),
21-
/// Writes [`Component`] with [`ComponentId`]
21+
/// Writes [`Component`](crate::prelude::Component) with [`ComponentId`]
2222
Write(ComponentId),
23-
/// Potentially reads all [`Component`]'s in the [`World`]
23+
/// Potentially reads all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)
2424
ReadAll,
25-
/// Potentially writes all [`Component`]'s in the [`World`]
25+
/// Potentially writes all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)
2626
WriteAll,
27-
/// [`FilteredEntityRef`] captures it's access at the `SystemParam` level, so will
28-
/// not conflict with other `QueryData` in the same Query
27+
/// [`FilteredEntityRef`](crate::world::FilteredEntityRef) captures it's access at the `SystemParam` level, so will
28+
/// not conflict with other [`QueryData`] in the same Query
2929
FilteredReadAll,
30-
/// [`FilteredEntityMut`] captures it's access at the `SystemParam` level, so will
31-
/// not conflict with other `QueryData` in the same Query
30+
/// [`FilteredEntityMut`](crate::world::FilteredEntityMut) captures it's access at the `SystemParam` level, so will
31+
/// not conflict with other [`QueryData`] in the same Query
3232
FilteredWriteAll,
3333
/// Potentially reads all [`Components`]'s except [`ComponentId`]
3434
ReadAllExcept {
@@ -47,7 +47,7 @@ pub enum EcsAccessLevel {
4747
}
4848

4949
/// Access level needed by [`QueryData`] fetch to the resource.
50-
#[derive(Copy, Clone)]
50+
#[derive(Copy, Clone, Debug)]
5151
pub enum ResourceAccessLevel {
5252
/// Reads the resource with [`ComponentId`]
5353
Read(ComponentId),
@@ -93,7 +93,7 @@ impl EcsAccessType {
9393
}
9494
}
9595

96-
/// Returns true if the access between `self` and `other` do not conflict.
96+
/// See [`AccessCompatible`] for more info
9797
pub fn is_compatible(&self, other: Self) -> AccessCompatible {
9898
use EcsAccessLevel::*;
9999
use EcsAccessType::*;
@@ -206,21 +206,52 @@ impl EcsAccessType {
206206
}
207207
}
208208

209+
/// Error returned from [`has_conflicts`].
210+
#[derive(Clone, Copy, Debug)]
211+
pub enum AccessError {
212+
ComponentNotRegistered,
213+
Conflict(EcsAccessType, EcsAccessType),
214+
EntityDoesNotMatch,
215+
}
216+
217+
impl core::error::Error for AccessError {}
218+
219+
impl core::fmt::Display for AccessError {
220+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
221+
match *self {
222+
AccessError::ComponentNotRegistered => {
223+
write!(
224+
f,
225+
"At least one component in Q was not registered in world.
226+
Consider calling `World::register_component`"
227+
)
228+
}
229+
AccessError::Conflict(ecs_access, ecs_access1) => {
230+
write!(f, "Conflict between {ecs_access:?} and {ecs_access1:?}")
231+
}
232+
AccessError::EntityDoesNotMatch => {
233+
write!(f, "Entity does not match Q")
234+
}
235+
}
236+
}
237+
}
238+
209239
/// Check if `Q` has any internal conflicts.
210-
pub fn has_conflicts<Q: QueryData>(components: &Components) -> bool {
240+
pub fn has_conflicts<Q: QueryData>(components: &Components) -> Result<(), AccessError> {
211241
let mut index_outer = 0;
212242
for (i, access) in Q::iter_access(components, &mut index_outer).enumerate() {
213243
let mut index_inner = 0;
214244
let mut except_index = None;
215245
let mut except_compatible = false;
246+
let mut last_access = None;
216247
for (j, access_other) in Q::iter_access(components, &mut index_inner).enumerate() {
248+
last_access = access_other;
217249
// don't check for conflicts when the access is the same access
218250
if i == j {
219251
continue;
220252
}
221253
let (Some(access), Some(access_other)) = (access, access_other) else {
222-
// A component wasn't registered
223-
return true;
254+
return Err(AccessError::ComponentNotRegistered);
224255
};
225256

226257
// if we're in an except sequence, check if the sequence has ended
@@ -233,7 +264,7 @@ pub fn has_conflicts<Q: QueryData>(components: &Components) -> bool {
233264

234265
if sequence_ended {
235266
if !except_compatible {
236-
return true;
267+
return Err(AccessError::Conflict(access, access_other));
237268
}
238269
except_compatible = false;
239270
except_index = None;
@@ -248,18 +279,20 @@ pub fn has_conflicts<Q: QueryData>(components: &Components) -> bool {
248279
except_index = Some(index);
249280
except_compatible = true;
250281
},
251-
AccessCompatible::Conflicts => return true,
282+
AccessCompatible::Conflicts => return Err(AccessError::Conflict(access, access_other)),
252283
AccessCompatible::ConflictsExceptSecond(index) => {
253284
except_index = Some(index);
254285
}
255286
}
256287
}
257288

258289
if except_index.is_some() && !except_compatible {
259-
return true;
290+
if let (Some(access), Some(access_other)) = (access, last_access) {
291+
return Err(AccessError::Conflict(access, access_other));
292+
}
260293
}
261294
}
262-
false
295+
Ok(())
263296
}
264297

265298
#[cfg(test)]
@@ -284,14 +317,23 @@ mod tests {
284317
let c = world.components();
285318

286319
// Compatible
287-
assert!(!has_conflicts::<&mut C1>(c));
288-
assert!(!has_conflicts::<&C1>(c));
289-
assert!(!has_conflicts::<(&C1, &C1)>(c));
320+
assert!(has_conflicts::<&mut C1>(c).is_ok());
321+
assert!(has_conflicts::<&C1>(c).is_ok());
322+
assert!(has_conflicts::<(&C1, &C1)>(c).is_ok());
290323

291324
// Conflicts
292-
assert!(has_conflicts::<(&C1, &mut C1)>(c));
293-
assert!(has_conflicts::<(&mut C1, &C1)>(c));
294-
assert!(has_conflicts::<(&mut C1, &mut C1)>(c));
325+
assert!(matches!(
326+
has_conflicts::<(&C1, &mut C1)>(c),
327+
Err(AccessError::Conflict(_, _))
328+
));
329+
assert!(matches!(
330+
has_conflicts::<(&mut C1, &C1)>(c),
331+
Err(AccessError::Conflict(_, _))
332+
));
333+
assert!(matches!(
334+
has_conflicts::<(&mut C1, &mut C1)>(c),
335+
Err(AccessError::Conflict(_, _))
336+
));
295337
}
296338

297339
#[test]
@@ -302,19 +344,43 @@ mod tests {
302344
let c = world.components();
303345

304346
// Compatible
305-
assert!(!has_conflicts::<(EntityRef, &C1)>(c));
306-
assert!(!has_conflicts::<(&C1, EntityRef)>(c));
307-
assert!(!has_conflicts::<(EntityRef, EntityRef)>(c));
347+
assert!(has_conflicts::<(EntityRef, &C1)>(c).is_ok());
348+
assert!(has_conflicts::<(&C1, EntityRef)>(c).is_ok());
349+
assert!(has_conflicts::<(EntityRef, EntityRef)>(c).is_ok());
308350

309351
// Conflicts
310-
assert!(has_conflicts::<(EntityRef, &mut C1)>(c));
311-
assert!(has_conflicts::<(&mut C1, EntityRef)>(c));
312-
assert!(has_conflicts::<(EntityMut, &C1)>(c));
313-
assert!(has_conflicts::<(&C1, EntityMut)>(c));
314-
assert!(has_conflicts::<(EntityMut, &mut C1)>(c));
315-
assert!(has_conflicts::<(&mut C1, EntityMut)>(c));
316-
assert!(has_conflicts::<(EntityMut, EntityRef)>(c));
317-
assert!(has_conflicts::<(EntityRef, EntityMut)>(c));
352+
assert!(matches!(
353+
has_conflicts::<(EntityRef, &mut C1)>(c),
354+
Err(AccessError::Conflict(_, _))
355+
));
356+
assert!(matches!(
357+
has_conflicts::<(&mut C1, EntityRef)>(c),
358+
Err(AccessError::Conflict(_, _))
359+
));
360+
assert!(matches!(
361+
has_conflicts::<(EntityMut, &C1)>(c),
362+
Err(AccessError::Conflict(_, _))
363+
));
364+
assert!(matches!(
365+
has_conflicts::<(&C1, EntityMut)>(c),
366+
Err(AccessError::Conflict(_, _))
367+
));
368+
assert!(matches!(
369+
has_conflicts::<(EntityMut, &mut C1)>(c),
370+
Err(AccessError::Conflict(_, _))
371+
));
372+
assert!(matches!(
373+
has_conflicts::<(&mut C1, EntityMut)>(c),
374+
Err(AccessError::Conflict(_, _))
375+
));
376+
assert!(matches!(
377+
has_conflicts::<(EntityMut, EntityRef)>(c),
378+
Err(AccessError::Conflict(_, _))
379+
));
380+
assert!(matches!(
381+
has_conflicts::<(EntityRef, EntityMut)>(c),
382+
Err(AccessError::Conflict(_, _))
383+
));
318384
}
319385

320386
#[test]
@@ -325,30 +391,24 @@ mod tests {
325391
let c = world.components();
326392

327393
// Compatible
328-
assert!(!has_conflicts::<(EntityRefExcept<C1>, &mut C1)>(c));
329-
assert!(!has_conflicts::<(&mut C1, EntityRefExcept<C1>)>(c));
330-
assert!(!has_conflicts::<(&C2, EntityRefExcept<C1>)>(c));
331-
assert!(!has_conflicts::<(&mut C1, EntityRefExcept<(C1, C2)>,)>(c));
332-
assert!(!has_conflicts::<(EntityRefExcept<(C1, C2)>, &mut C1,)>(c));
333-
assert!(!has_conflicts::<(
334-
&mut C1,
335-
&mut C2,
336-
EntityRefExcept<(C1, C2)>,
337-
)>(c));
338-
assert!(!has_conflicts::<(
339-
&mut C1,
340-
EntityRefExcept<(C1, C2)>,
341-
&mut C2,
342-
)>(c));
343-
assert!(!has_conflicts::<(
344-
EntityRefExcept<(C1, C2)>,
345-
&mut C1,
346-
&mut C2,
347-
)>(c));
394+
assert!(has_conflicts::<(EntityRefExcept<C1>, &mut C1)>(c).is_ok());
395+
assert!(has_conflicts::<(&mut C1, EntityRefExcept<C1>)>(c).is_ok());
396+
assert!(has_conflicts::<(&C2, EntityRefExcept<C1>)>(c).is_ok());
397+
assert!(has_conflicts::<(&mut C1, EntityRefExcept<(C1, C2)>,)>(c).is_ok());
398+
assert!(has_conflicts::<(EntityRefExcept<(C1, C2)>, &mut C1,)>(c).is_ok());
399+
assert!(has_conflicts::<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>,)>(c).is_ok());
400+
assert!(has_conflicts::<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2,)>(c).is_ok());
401+
assert!(has_conflicts::<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2,)>(c).is_ok());
348402

349403
// Conflicts
350-
assert!(has_conflicts::<(EntityRefExcept<C1>, &mut C2)>(c));
351-
assert!(has_conflicts::<(&mut C2, EntityRefExcept<C1>)>(c));
404+
assert!(matches!(
405+
has_conflicts::<(EntityRefExcept<C1>, &mut C2)>(c),
406+
Err(AccessError::Conflict(_, _))
407+
));
408+
assert!(matches!(
409+
has_conflicts::<(&mut C2, EntityRefExcept<C1>)>(c),
410+
Err(AccessError::Conflict(_, _))
411+
));
352412
}
353413

354414
#[test]
@@ -359,30 +419,30 @@ mod tests {
359419
let c = world.components();
360420

361421
// Compatible
362-
assert!(!has_conflicts::<(EntityMutExcept<C1>, &mut C1)>(c));
363-
assert!(!has_conflicts::<(&mut C1, EntityMutExcept<C1>)>(c));
364-
assert!(!has_conflicts::<(&mut C1, EntityMutExcept<(C1, C2)>,)>(c));
365-
assert!(!has_conflicts::<(EntityMutExcept<(C1, C2)>, &mut C1,)>(c));
366-
assert!(!has_conflicts::<(
367-
&mut C1,
368-
&mut C2,
369-
EntityMutExcept<(C1, C2)>,
370-
)>(c));
371-
assert!(!has_conflicts::<(
372-
&mut C1,
373-
EntityMutExcept<(C1, C2)>,
374-
&mut C2,
375-
)>(c));
376-
assert!(!has_conflicts::<(
377-
EntityMutExcept<(C1, C2)>,
378-
&mut C1,
379-
&mut C2,
380-
)>(c));
422+
assert!(has_conflicts::<(EntityMutExcept<C1>, &mut C1)>(c).is_ok());
423+
assert!(has_conflicts::<(&mut C1, EntityMutExcept<C1>)>(c).is_ok());
424+
assert!(has_conflicts::<(&mut C1, EntityMutExcept<(C1, C2)>,)>(c).is_ok());
425+
assert!(has_conflicts::<(EntityMutExcept<(C1, C2)>, &mut C1,)>(c).is_ok());
426+
assert!(has_conflicts::<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>,)>(c).is_ok());
427+
assert!(has_conflicts::<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2,)>(c).is_ok());
428+
assert!(has_conflicts::<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2,)>(c).is_ok());
381429

382430
// Conflicts
383-
assert!(has_conflicts::<(&C2, EntityMutExcept<C1>)>(c));
384-
assert!(has_conflicts::<(EntityMutExcept<C1>, &C2)>(c));
385-
assert!(has_conflicts::<(EntityMutExcept<C1>, &mut C2)>(c));
386-
assert!(has_conflicts::<(&mut C2, EntityMutExcept<C1>)>(c));
431+
assert!(matches!(
432+
has_conflicts::<(&C2, EntityMutExcept<C1>)>(c),
433+
Err(AccessError::Conflict(_, _))
434+
));
435+
assert!(matches!(
436+
has_conflicts::<(EntityMutExcept<C1>, &C2)>(c),
437+
Err(AccessError::Conflict(_, _))
438+
));
439+
assert!(matches!(
440+
has_conflicts::<(EntityMutExcept<C1>, &mut C2)>(c),
441+
Err(AccessError::Conflict(_, _))
442+
));
443+
assert!(matches!(
444+
has_conflicts::<(&mut C2, EntityMutExcept<C1>)>(c),
445+
Err(AccessError::Conflict(_, _))
446+
));
387447
}
388448
}

crates/bevy_ecs/src/world/entity_access/entity_mut.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
change_detection::{ComponentTicks, MaybeLocation, Tick},
44
component::{Component, ComponentId, Mutable},
55
entity::{ContainsEntity, Entity, EntityEquivalent, EntityLocation},
6-
query::{has_conflicts, Access, ReadOnlyQueryData, ReleaseStateQueryData},
6+
query::{has_conflicts, Access, AccessError, ReadOnlyQueryData, ReleaseStateQueryData},
77
world::{
88
error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, DynamicComponentFetch,
99
EntityRef, FilteredEntityMut, FilteredEntityRef, Mut, Ref,
@@ -192,12 +192,12 @@ impl<'w> EntityMut<'w> {
192192
}
193193

194194
/// returns None if component wasn't registered, or if the access is not compatible bewteen terms
195-
pub fn get_components_mut<Q: ReleaseStateQueryData>(&mut self) -> Option<Q::Item<'_, 'static>> {
196-
if has_conflicts::<Q>(self.cell.world().components()) {
197-
return None;
198-
}
195+
pub fn get_components_mut<Q: ReleaseStateQueryData>(
196+
&mut self,
197+
) -> Result<Q::Item<'_, 'static>, AccessError> {
198+
has_conflicts::<Q>(self.cell.world().components())?;
199199
// SAFETY: we checked that there were not conflicting components above
200-
unsafe { self.get_components_mut_unchecked::<Q>() }
200+
unsafe { self.get_components_mut_unchecked::<Q>() }.ok_or(AccessError::EntityDoesNotMatch)
201201
}
202202

203203
/// Consumes self and returns components for the current entity that match the query `Q` for the world lifetime `'w`,

0 commit comments

Comments
 (0)