bevy_ecs/system/
access.rs1use alloc::borrow::Cow;
2use bevy_utils::prelude::ShortName;
3
4use crate::{
5 query::{AccessConflicts, FilteredAccess, FilteredAccessSet},
6 system::SystemMeta,
7};
8
9#[derive(Clone, Debug, Default, PartialEq, Eq)]
14pub enum SystemAccess {
15 #[default]
17 None,
18 Shared(FilteredAccessSet),
22 Exclusive,
28}
29
30impl SystemAccess {
31 pub fn is_none(&self) -> bool {
34 matches!(self, Self::None)
35 }
36
37 pub fn is_shared(&self) -> bool {
41 matches!(self, Self::Shared(_))
42 }
43
44 pub fn is_exclusive(&self) -> bool {
46 matches!(self, Self::Exclusive)
47 }
48
49 pub fn is_compatible(&self, other: &Self) -> bool {
53 match (self, other) {
54 (Self::None, _) | (_, Self::None) => true,
55 (Self::Shared(access), Self::Shared(other_access)) => {
56 access.is_compatible(other_access)
57 }
58 (Self::Exclusive, _) | (_, Self::Exclusive) => false,
59 }
60 }
61
62 pub fn get_conflicts_single(&self, filtered_access: &FilteredAccess) -> AccessConflicts {
76 match self {
77 SystemAccess::None => AccessConflicts::empty(),
78 SystemAccess::Shared(access) => access.get_conflicts_single(filtered_access),
79 SystemAccess::Exclusive => AccessConflicts::All,
80 }
81 }
82
83 pub fn get_conflicts(&self, other: &Self) -> AccessConflicts {
97 match (self, other) {
98 (SystemAccess::None, _) | (_, SystemAccess::None) => AccessConflicts::empty(),
99 (SystemAccess::Shared(access), SystemAccess::Shared(other_access)) => {
100 access.get_conflicts(other_access)
101 }
102 (SystemAccess::Exclusive, _) | (_, SystemAccess::Exclusive) => AccessConflicts::All,
103 }
104 }
105
106 pub fn to_filtered_access_set(&self) -> Cow<'_, FilteredAccessSet> {
108 match self {
109 Self::None => Cow::Owned(FilteredAccessSet::new()),
110 Self::Shared(access) => Cow::Borrowed(access),
111 Self::Exclusive => {
112 let mut access_set = FilteredAccessSet::new();
113 let mut access = FilteredAccess::default();
114 access.write_all();
115 access_set.add(access);
116 Cow::Owned(access_set)
117 }
118 }
119 }
120
121 pub fn require_shared_access<T>(&mut self, system_meta: &SystemMeta) -> &mut FilteredAccessSet {
174 match self {
175 this @ Self::None => {
176 *this = Self::Shared(FilteredAccessSet::new());
177 if let Self::Shared(access) = this {
178 access
179 } else {
180 unreachable!()
181 }
182 }
183 Self::Shared(access) => access,
184 Self::Exclusive => panic!(
185 "error[B0002]: {} in system {} conflicts with a previous system parameter.",
186 ShortName::of::<T>(),
187 system_meta.name()
188 ),
189 }
190 }
191
192 pub fn require_exclusive_access<T>(&mut self, system_meta: &SystemMeta) {
206 if !matches!(self, Self::None) {
207 panic!(
208 "error[B0002]: {} in system {} conflicts with a previous system parameter.",
209 ShortName::of::<T>(),
210 system_meta.name()
211 );
212 }
213 *self = Self::Exclusive;
214 }
215
216 pub fn try_add(&mut self, filtered_access: FilteredAccess) -> Result<(), AccessConflicts> {
236 let conflicts = self.get_conflicts_single(&filtered_access);
237 self.ensure_filtered_access(filtered_access);
238 if conflicts.is_empty() {
239 Ok(())
240 } else {
241 Err(conflicts)
242 }
243 }
244
245 pub fn ensure_filtered_access(&mut self, filtered_access: FilteredAccess) {
250 self.ensure_metadata_access();
251 if let Self::Shared(access) = self {
254 access.add(filtered_access);
255 }
256 }
257
258 pub fn ensure_metadata_access(&mut self) {
265 if let Self::None = self {
268 *self = Self::Shared(FilteredAccessSet::new());
269 }
270 }
271
272 pub fn extend(&mut self, other: Self) {
274 match (&mut *self, other) {
275 (_, Self::None) | (Self::Exclusive, _) => {
276 }
278 (Self::None, Self::Shared(other_access)) => {
279 *self = Self::Shared(other_access);
281 }
282 (Self::Shared(access), Self::Shared(other_access)) => {
283 access.extend(other_access);
285 }
286 (_, Self::Exclusive) => {
287 *self = Self::Exclusive;
289 }
290 }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use crate::{
297 component::ComponentIds,
298 query::{FilteredAccess, FilteredAccessSet},
299 system::{SystemAccess, SystemMeta},
300 };
301
302 #[test]
303 fn check_default_access() {
304 let mut access = SystemAccess::default();
305
306 assert_eq!(access, SystemAccess::None);
307 assert!(access.is_none());
308 assert_ne!(access, SystemAccess::Shared(FilteredAccessSet::default()));
309 assert!(!access.is_shared());
310 assert_ne!(access, SystemAccess::Exclusive);
311 assert!(!access.is_exclusive());
312
313 access.ensure_metadata_access();
314
315 assert!(access.is_shared());
316 assert_eq!(access, SystemAccess::Shared(FilteredAccessSet::default()));
317 }
318
319 #[test]
320 fn check_shared_access() {
321 let mut access = SystemAccess::Shared(FilteredAccessSet::default());
322
323 assert_ne!(access, SystemAccess::None);
324 assert!(!access.is_none());
325 assert!(access.is_shared());
326 assert_ne!(access, SystemAccess::Exclusive);
327 assert!(!access.is_exclusive());
328
329 access.ensure_metadata_access();
330
331 assert!(access.is_shared());
332
333 access.ensure_filtered_access(FilteredAccess::default());
334
335 assert!(access.is_shared());
336 }
337
338 #[test]
339 fn check_exclusive_access() {
340 let mut access = SystemAccess::Exclusive;
341
342 assert_ne!(access, SystemAccess::None);
343 assert!(!access.is_none());
344 assert_ne!(access, SystemAccess::Shared(FilteredAccessSet::default()));
345 assert!(!access.is_shared());
346 assert!(access.is_exclusive());
347
348 access.ensure_metadata_access();
349
350 assert!(access.is_exclusive());
351
352 access.ensure_filtered_access(FilteredAccess::default());
353
354 assert!(access.is_exclusive());
355 }
356
357 #[test]
358 fn check_compatibility() {
359 let mut ids = ComponentIds::default();
360 let access_none = SystemAccess::None;
361 let access_shared = SystemAccess::Shared({
362 let mut set = FilteredAccessSet::default();
363 set.add_unfiltered_component_read(ids.next_mut());
364 set
365 });
366 let access_exclusive = SystemAccess::Exclusive;
367
368 assert!(access_none.is_compatible(&access_none));
369 assert!(access_none.is_compatible(&access_shared));
370 assert!(access_none.is_compatible(&access_exclusive));
371
372 assert!(access_shared.is_compatible(&access_none));
373 assert!(access_shared.is_compatible(&access_shared));
374 assert!(!access_shared.is_compatible(&access_exclusive));
375
376 assert!(access_exclusive.is_compatible(&access_none));
377 assert!(!access_exclusive.is_compatible(&access_shared));
378 assert!(!access_exclusive.is_compatible(&access_exclusive));
379 }
380
381 #[test]
382 fn conflict_reporting() {
383 let mut ids = ComponentIds::default();
384 let access_none = SystemAccess::None;
385 let access_shared = SystemAccess::Shared({
386 let mut set = FilteredAccessSet::default();
387 set.add_unfiltered_component_read(ids.next_mut());
388 set
389 });
390 let access_exclusive = SystemAccess::Exclusive;
391
392 assert!(access_none.get_conflicts(&access_none).is_empty());
393 assert!(access_none.get_conflicts(&access_shared).is_empty());
394 assert!(access_none.get_conflicts(&access_exclusive).is_empty());
395
396 assert!(access_shared.get_conflicts(&access_none).is_empty());
397 assert!(access_shared.get_conflicts(&access_shared).is_empty());
398 assert_eq!(
399 access_shared.get_conflicts(&access_exclusive),
400 crate::query::AccessConflicts::All
401 );
402
403 assert!(access_exclusive.get_conflicts(&access_none).is_empty());
404 assert_eq!(
405 access_exclusive.get_conflicts(&access_shared),
406 crate::query::AccessConflicts::All
407 );
408 assert_eq!(
409 access_exclusive.get_conflicts(&access_exclusive),
410 crate::query::AccessConflicts::All
411 );
412 }
413
414 #[test]
415 #[should_panic]
416 fn require_shared_access_panics_on_exclusive() {
417 let mut access = SystemAccess::Exclusive;
418 access.require_shared_access::<()>(&SystemMeta::new::<()>());
419 }
420
421 #[test]
422 #[should_panic]
423 fn require_exclusive_access_panics_on_shared() {
424 let mut access = SystemAccess::Shared(FilteredAccessSet::default());
425 access.require_exclusive_access::<()>(&SystemMeta::new::<()>());
426 }
427
428 #[test]
429 #[should_panic]
430 fn require_exclusive_access_panics_on_exclusive() {
431 let mut access = SystemAccess::Exclusive;
432 access.require_exclusive_access::<()>(&SystemMeta::new::<()>());
433 }
434
435 #[test]
436 fn try_add_returns_correctly() {
437 let mut access = SystemAccess::None;
438 let filtered_access = FilteredAccess::default();
439
440 assert!(access.try_add(filtered_access.clone()).is_ok());
441 assert!(access.is_shared());
442
443 let mut access_shared = SystemAccess::Shared(FilteredAccessSet::default());
444 assert!(access_shared.try_add(filtered_access.clone()).is_ok());
445
446 let mut access_exclusive = SystemAccess::Exclusive;
447 assert!(access_exclusive.try_add(filtered_access).is_err());
448 }
449
450 #[test]
451 fn conversion_to_access_sets() {
452 let mut ids = ComponentIds::default();
453 let id_1 = ids.next_mut();
454
455 let access_none = SystemAccess::None;
456 let access_shared = SystemAccess::Shared({
457 let mut set = FilteredAccessSet::default();
458 set.add_unfiltered_component_read(id_1);
459 set
460 });
461 let access_exclusive = SystemAccess::Exclusive;
462
463 assert_eq!(
464 access_none.to_filtered_access_set().into_owned(),
465 FilteredAccessSet::new()
466 );
467 assert_eq!(access_shared.to_filtered_access_set().into_owned(), {
468 let mut set = FilteredAccessSet::default();
469 set.add_unfiltered_component_read(id_1);
470 set
471 });
472 assert_eq!(access_exclusive.to_filtered_access_set().into_owned(), {
473 let mut set = FilteredAccessSet::new();
474 let mut access = FilteredAccess::default();
475 access.write_all();
476 set.add(access);
477 set
478 });
479 }
480
481 #[test]
482 fn extending_access() {
483 let mut ids = ComponentIds::default();
484 let mut access = SystemAccess::default();
485
486 let access_none = SystemAccess::None;
487 let access_shared = SystemAccess::Shared({
488 let mut set = FilteredAccessSet::default();
489 set.add_unfiltered_component_read(ids.next_mut());
490 set
491 });
492 let access_exclusive = SystemAccess::Exclusive;
493
494 access.extend(access_none.clone());
495 assert_eq!(access, SystemAccess::None);
496
497 access.extend(access_shared.clone());
498 assert_eq!(access, access_shared);
499
500 access.extend(access_exclusive.clone());
501 assert_eq!(access, access_exclusive);
502 }
503}