1use alloc::boxed::Box;
2use bevy_utils::prelude::DebugName;
3use core::{
4 any::TypeId,
5 fmt::Debug,
6 hash::{Hash, Hasher},
7 marker::PhantomData,
8};
9
10pub use crate::label::DynEq;
11pub use bevy_ecs_macros::{ScheduleLabel, SystemSet};
12
13use crate::{
14 define_label,
15 intern::Interned,
16 system::{
17 ExclusiveSystemParamFunction, FromInput, IntoResult, IsExclusiveFunctionSystem,
18 IsFunctionSystem, SystemParamFunction,
19 },
20};
21
22define_label!(
23 #[diagnostic::on_unimplemented(
57 note = "consider annotating `{Self}` with `#[derive(ScheduleLabel)]`"
58 )]
59 ScheduleLabel,
60 SCHEDULE_LABEL_INTERNER
61);
62
63define_label!(
64 #[diagnostic::on_unimplemented(
152 note = "consider annotating `{Self}` with `#[derive(SystemSet)]`"
153 )]
154 SystemSet,
155 SYSTEM_SET_INTERNER,
156 extra_methods: {
157 fn system_type(&self) -> Option<TypeId> {
159 None
160 }
161
162 fn is_anonymous(&self) -> bool {
164 false
165 }
166 },
167 extra_methods_impl: {
168 fn system_type(&self) -> Option<TypeId> {
169 (**self).system_type()
170 }
171
172 fn is_anonymous(&self) -> bool {
173 (**self).is_anonymous()
174 }
175 }
176);
177
178pub type InternedSystemSet = Interned<dyn SystemSet>;
180pub type InternedScheduleLabel = Interned<dyn ScheduleLabel>;
182
183pub struct SystemTypeSet<T: 'static>(PhantomData<fn() -> T>);
190
191impl<T: 'static> SystemTypeSet<T> {
192 pub(crate) fn new() -> Self {
193 Self(PhantomData)
194 }
195}
196
197impl<T> Debug for SystemTypeSet<T> {
198 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 write!(f, "SystemTypeSet:{}", DebugName::type_name::<T>())
200 }
201}
202
203impl<T> Hash for SystemTypeSet<T> {
204 fn hash<H: Hasher>(&self, _state: &mut H) {
205 }
207}
208
209impl<T> Clone for SystemTypeSet<T> {
210 fn clone(&self) -> Self {
211 *self
212 }
213}
214
215impl<T> Copy for SystemTypeSet<T> {}
216
217impl<T> PartialEq for SystemTypeSet<T> {
218 #[inline]
219 fn eq(&self, _other: &Self) -> bool {
220 true
222 }
223}
224
225impl<T> Eq for SystemTypeSet<T> {}
226
227impl<T> SystemSet for SystemTypeSet<T> {
228 fn system_type(&self) -> Option<TypeId> {
229 Some(TypeId::of::<T>())
230 }
231
232 fn dyn_clone(&self) -> Box<dyn SystemSet> {
233 Box::new(*self)
234 }
235}
236
237#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
241pub struct AnonymousSet(usize);
242
243impl AnonymousSet {
244 pub(crate) fn new(id: usize) -> Self {
245 Self(id)
246 }
247}
248
249impl SystemSet for AnonymousSet {
250 fn is_anonymous(&self) -> bool {
251 true
252 }
253
254 fn dyn_clone(&self) -> Box<dyn SystemSet> {
255 Box::new(*self)
256 }
257}
258
259#[diagnostic::on_unimplemented(
267 message = "`{Self}` is not a system set",
268 label = "invalid system set"
269)]
270pub trait IntoSystemSet<Marker>: Sized {
271 type Set: SystemSet;
273
274 fn into_system_set(self) -> Self::Set;
276}
277
278impl<S: SystemSet> IntoSystemSet<()> for S {
280 type Set = Self;
281
282 #[inline]
283 fn into_system_set(self) -> Self::Set {
284 self
285 }
286}
287
288impl<Marker, F> IntoSystemSet<(IsFunctionSystem, Marker)> for F
290where
291 Marker: 'static,
292 F: SystemParamFunction<Marker, In: FromInput<()>, Out: IntoResult<()>>,
293{
294 type Set = SystemTypeSet<F>;
295
296 #[inline]
297 fn into_system_set(self) -> Self::Set {
298 SystemTypeSet::<F>::new()
299 }
300}
301
302impl<Marker, F> IntoSystemSet<(IsExclusiveFunctionSystem, Marker)> for F
304where
305 Marker: 'static,
306 F::Out: IntoResult<()>,
307 F: ExclusiveSystemParamFunction<Marker>,
308{
309 type Set = SystemTypeSet<F>;
310
311 #[inline]
312 fn into_system_set(self) -> Self::Set {
313 SystemTypeSet::<F>::new()
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use crate::{
320 resource::Resource,
321 schedule::{tests::ResMut, Schedule},
322 system::{IntoSystem, System},
323 };
324
325 use super::*;
326
327 #[test]
328 fn test_schedule_label() {
329 use crate::world::World;
330
331 #[derive(Resource)]
332 struct Flag(bool);
333
334 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
335 struct A;
336
337 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
338 struct B;
339
340 let mut world = World::new();
341
342 let mut schedule = Schedule::new(A);
343 schedule.add_systems(|mut flag: ResMut<Flag>| flag.0 = true);
344 world.add_schedule(schedule);
345
346 let interned = A.intern();
347
348 world.insert_resource(Flag(false));
349 world.run_schedule(interned);
350 assert!(world.resource::<Flag>().0);
351
352 world.insert_resource(Flag(false));
353 world.run_schedule(interned);
354 assert!(world.resource::<Flag>().0);
355
356 assert_ne!(A.intern(), B.intern());
357 }
358
359 #[test]
360 fn test_derive_schedule_label() {
361 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
362 struct UnitLabel;
363
364 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
365 struct TupleLabel(u32, u32);
366
367 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
368 struct StructLabel {
369 a: u32,
370 b: u32,
371 }
372
373 #[expect(
374 dead_code,
375 reason = "This is a derive macro compilation test. It won't be constructed."
376 )]
377 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
378 struct EmptyTupleLabel();
379
380 #[expect(
381 dead_code,
382 reason = "This is a derive macro compilation test. It won't be constructed."
383 )]
384 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
385 struct EmptyStructLabel {}
386
387 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
388 enum EnumLabel {
389 #[default]
390 Unit,
391 Tuple(u32, u32),
392 Struct {
393 a: u32,
394 b: u32,
395 },
396 }
397
398 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
399 struct GenericLabel<T>(PhantomData<T>);
400
401 assert_eq!(UnitLabel.intern(), UnitLabel.intern());
402 assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
403 assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
404 assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
405 assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());
406
407 assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
408 assert_eq!(
409 EnumLabel::Tuple(0, 0).intern(),
410 EnumLabel::Tuple(0, 0).intern()
411 );
412 assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
413 assert_ne!(
414 EnumLabel::Tuple(0, 0).intern(),
415 EnumLabel::Tuple(0, 1).intern()
416 );
417 assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
418 assert_ne!(
419 TupleLabel(0, 0).intern(),
420 StructLabel { a: 0, b: 0 }.intern()
421 );
422 assert_ne!(
423 EnumLabel::Tuple(0, 0).intern(),
424 EnumLabel::Struct { a: 0, b: 0 }.intern()
425 );
426
427 assert_eq!(
428 StructLabel { a: 0, b: 0 }.intern(),
429 StructLabel { a: 0, b: 0 }.intern()
430 );
431 assert_eq!(
432 EnumLabel::Struct { a: 0, b: 0 }.intern(),
433 EnumLabel::Struct { a: 0, b: 0 }.intern()
434 );
435 assert_ne!(
436 StructLabel { a: 0, b: 0 }.intern(),
437 StructLabel { a: 0, b: 1 }.intern()
438 );
439 assert_ne!(
440 EnumLabel::Struct { a: 0, b: 0 }.intern(),
441 EnumLabel::Struct { a: 0, b: 1 }.intern()
442 );
443 assert_ne!(
444 StructLabel { a: 0, b: 0 }.intern(),
445 EnumLabel::Struct { a: 0, b: 0 }.intern()
446 );
447 assert_ne!(
448 StructLabel { a: 0, b: 0 }.intern(),
449 EnumLabel::Struct { a: 0, b: 0 }.intern()
450 );
451 assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
452 assert_ne!(
453 EnumLabel::Struct { a: 0, b: 0 }.intern(),
454 EnumLabel::Unit.intern()
455 );
456
457 assert_eq!(
458 GenericLabel::<u32>(PhantomData).intern(),
459 GenericLabel::<u32>(PhantomData).intern()
460 );
461 assert_ne!(
462 GenericLabel::<u32>(PhantomData).intern(),
463 GenericLabel::<u64>(PhantomData).intern()
464 );
465 }
466
467 #[test]
468 fn test_derive_system_set() {
469 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
470 struct UnitSet;
471
472 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
473 struct TupleSet(u32, u32);
474
475 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
476 struct StructSet {
477 a: u32,
478 b: u32,
479 }
480
481 #[expect(
482 dead_code,
483 reason = "This is a derive macro compilation test. It won't be constructed."
484 )]
485 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
486 struct EmptyTupleSet();
487
488 #[expect(
489 dead_code,
490 reason = "This is a derive macro compilation test. It won't be constructed."
491 )]
492 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
493 struct EmptyStructSet {}
494
495 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
496 enum EnumSet {
497 #[default]
498 Unit,
499 Tuple(u32, u32),
500 Struct {
501 a: u32,
502 b: u32,
503 },
504 }
505
506 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
507 struct GenericSet<T>(PhantomData<T>);
508
509 assert_eq!(UnitSet.intern(), UnitSet.intern());
510 assert_eq!(EnumSet::Unit.intern(), EnumSet::Unit.intern());
511 assert_ne!(UnitSet.intern(), EnumSet::Unit.intern());
512 assert_ne!(UnitSet.intern(), TupleSet(0, 0).intern());
513 assert_ne!(EnumSet::Unit.intern(), EnumSet::Tuple(0, 0).intern());
514
515 assert_eq!(TupleSet(0, 0).intern(), TupleSet(0, 0).intern());
516 assert_eq!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
517 assert_ne!(TupleSet(0, 0).intern(), TupleSet(0, 1).intern());
518 assert_ne!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 1).intern());
519 assert_ne!(TupleSet(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
520 assert_ne!(TupleSet(0, 0).intern(), StructSet { a: 0, b: 0 }.intern());
521 assert_ne!(
522 EnumSet::Tuple(0, 0).intern(),
523 EnumSet::Struct { a: 0, b: 0 }.intern()
524 );
525
526 assert_eq!(
527 StructSet { a: 0, b: 0 }.intern(),
528 StructSet { a: 0, b: 0 }.intern()
529 );
530 assert_eq!(
531 EnumSet::Struct { a: 0, b: 0 }.intern(),
532 EnumSet::Struct { a: 0, b: 0 }.intern()
533 );
534 assert_ne!(
535 StructSet { a: 0, b: 0 }.intern(),
536 StructSet { a: 0, b: 1 }.intern()
537 );
538 assert_ne!(
539 EnumSet::Struct { a: 0, b: 0 }.intern(),
540 EnumSet::Struct { a: 0, b: 1 }.intern()
541 );
542 assert_ne!(
543 StructSet { a: 0, b: 0 }.intern(),
544 EnumSet::Struct { a: 0, b: 0 }.intern()
545 );
546 assert_ne!(
547 StructSet { a: 0, b: 0 }.intern(),
548 EnumSet::Struct { a: 0, b: 0 }.intern()
549 );
550 assert_ne!(StructSet { a: 0, b: 0 }.intern(), UnitSet.intern(),);
551 assert_ne!(
552 EnumSet::Struct { a: 0, b: 0 }.intern(),
553 EnumSet::Unit.intern()
554 );
555
556 assert_eq!(
557 GenericSet::<u32>(PhantomData).intern(),
558 GenericSet::<u32>(PhantomData).intern()
559 );
560 assert_ne!(
561 GenericSet::<u32>(PhantomData).intern(),
562 GenericSet::<u64>(PhantomData).intern()
563 );
564 }
565
566 #[test]
567 fn system_set_matches_default_system_set() {
568 fn system() {}
569 let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
570 let system = IntoSystem::into_system(system);
571 let set_from_system = system.default_system_sets()[0];
572 assert_eq!(set_from_into_system_set, set_from_system);
573 }
574
575 #[test]
576 fn system_set_matches_default_system_set_exclusive() {
577 fn system(_: &mut crate::world::World) {}
578 let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
579 let system = IntoSystem::into_system(system);
580 let set_from_system = system.default_system_sets()[0];
581 assert_eq!(set_from_into_system_set, set_from_system);
582 }
583}