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