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, FromInput, FunctionSystem,
18 IntoResult, 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: SystemParamFunction<Marker, In: FromInput<()>, Out: IntoResult<()>>,
295{
296 type Set = SystemTypeSet<FunctionSystem<Marker, (), (), F>>;
297
298 #[inline]
299 fn into_system_set(self) -> Self::Set {
300 SystemTypeSet::<FunctionSystem<Marker, (), (), F>>::new()
301 }
302}
303
304impl<Marker, F> IntoSystemSet<(IsExclusiveFunctionSystem, Marker)> for F
306where
307 Marker: 'static,
308 F::Out: IntoResult<()>,
309 F: ExclusiveSystemParamFunction<Marker>,
310{
311 type Set = SystemTypeSet<ExclusiveFunctionSystem<Marker, (), F>>;
312
313 #[inline]
314 fn into_system_set(self) -> Self::Set {
315 SystemTypeSet::<ExclusiveFunctionSystem<Marker, (), F>>::new()
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use crate::{
322 resource::Resource,
323 schedule::{tests::ResMut, Schedule},
324 system::{IntoSystem, System},
325 };
326
327 use super::*;
328
329 #[test]
330 fn test_schedule_label() {
331 use crate::world::World;
332
333 #[derive(Resource)]
334 struct Flag(bool);
335
336 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
337 struct A;
338
339 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
340 struct B;
341
342 let mut world = World::new();
343
344 let mut schedule = Schedule::new(A);
345 schedule.add_systems(|mut flag: ResMut<Flag>| flag.0 = true);
346 world.add_schedule(schedule);
347
348 let interned = A.intern();
349
350 world.insert_resource(Flag(false));
351 world.run_schedule(interned);
352 assert!(world.resource::<Flag>().0);
353
354 world.insert_resource(Flag(false));
355 world.run_schedule(interned);
356 assert!(world.resource::<Flag>().0);
357
358 assert_ne!(A.intern(), B.intern());
359 }
360
361 #[test]
362 fn test_derive_schedule_label() {
363 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
364 struct UnitLabel;
365
366 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
367 struct TupleLabel(u32, u32);
368
369 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
370 struct StructLabel {
371 a: u32,
372 b: u32,
373 }
374
375 #[expect(
376 dead_code,
377 reason = "This is a derive macro compilation test. It won't be constructed."
378 )]
379 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
380 struct EmptyTupleLabel();
381
382 #[expect(
383 dead_code,
384 reason = "This is a derive macro compilation test. It won't be constructed."
385 )]
386 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
387 struct EmptyStructLabel {}
388
389 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
390 enum EnumLabel {
391 #[default]
392 Unit,
393 Tuple(u32, u32),
394 Struct {
395 a: u32,
396 b: u32,
397 },
398 }
399
400 #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
401 struct GenericLabel<T>(PhantomData<T>);
402
403 assert_eq!(UnitLabel.intern(), UnitLabel.intern());
404 assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
405 assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
406 assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
407 assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());
408
409 assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
410 assert_eq!(
411 EnumLabel::Tuple(0, 0).intern(),
412 EnumLabel::Tuple(0, 0).intern()
413 );
414 assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
415 assert_ne!(
416 EnumLabel::Tuple(0, 0).intern(),
417 EnumLabel::Tuple(0, 1).intern()
418 );
419 assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
420 assert_ne!(
421 TupleLabel(0, 0).intern(),
422 StructLabel { a: 0, b: 0 }.intern()
423 );
424 assert_ne!(
425 EnumLabel::Tuple(0, 0).intern(),
426 EnumLabel::Struct { a: 0, b: 0 }.intern()
427 );
428
429 assert_eq!(
430 StructLabel { a: 0, b: 0 }.intern(),
431 StructLabel { a: 0, b: 0 }.intern()
432 );
433 assert_eq!(
434 EnumLabel::Struct { a: 0, b: 0 }.intern(),
435 EnumLabel::Struct { a: 0, b: 0 }.intern()
436 );
437 assert_ne!(
438 StructLabel { a: 0, b: 0 }.intern(),
439 StructLabel { a: 0, b: 1 }.intern()
440 );
441 assert_ne!(
442 EnumLabel::Struct { a: 0, b: 0 }.intern(),
443 EnumLabel::Struct { a: 0, b: 1 }.intern()
444 );
445 assert_ne!(
446 StructLabel { a: 0, b: 0 }.intern(),
447 EnumLabel::Struct { a: 0, b: 0 }.intern()
448 );
449 assert_ne!(
450 StructLabel { a: 0, b: 0 }.intern(),
451 EnumLabel::Struct { a: 0, b: 0 }.intern()
452 );
453 assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
454 assert_ne!(
455 EnumLabel::Struct { a: 0, b: 0 }.intern(),
456 EnumLabel::Unit.intern()
457 );
458
459 assert_eq!(
460 GenericLabel::<u32>(PhantomData).intern(),
461 GenericLabel::<u32>(PhantomData).intern()
462 );
463 assert_ne!(
464 GenericLabel::<u32>(PhantomData).intern(),
465 GenericLabel::<u64>(PhantomData).intern()
466 );
467 }
468
469 #[test]
470 fn test_derive_system_set() {
471 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
472 struct UnitSet;
473
474 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
475 struct TupleSet(u32, u32);
476
477 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
478 struct StructSet {
479 a: u32,
480 b: u32,
481 }
482
483 #[expect(
484 dead_code,
485 reason = "This is a derive macro compilation test. It won't be constructed."
486 )]
487 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
488 struct EmptyTupleSet();
489
490 #[expect(
491 dead_code,
492 reason = "This is a derive macro compilation test. It won't be constructed."
493 )]
494 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
495 struct EmptyStructSet {}
496
497 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
498 enum EnumSet {
499 #[default]
500 Unit,
501 Tuple(u32, u32),
502 Struct {
503 a: u32,
504 b: u32,
505 },
506 }
507
508 #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
509 struct GenericSet<T>(PhantomData<T>);
510
511 assert_eq!(UnitSet.intern(), UnitSet.intern());
512 assert_eq!(EnumSet::Unit.intern(), EnumSet::Unit.intern());
513 assert_ne!(UnitSet.intern(), EnumSet::Unit.intern());
514 assert_ne!(UnitSet.intern(), TupleSet(0, 0).intern());
515 assert_ne!(EnumSet::Unit.intern(), EnumSet::Tuple(0, 0).intern());
516
517 assert_eq!(TupleSet(0, 0).intern(), TupleSet(0, 0).intern());
518 assert_eq!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
519 assert_ne!(TupleSet(0, 0).intern(), TupleSet(0, 1).intern());
520 assert_ne!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 1).intern());
521 assert_ne!(TupleSet(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
522 assert_ne!(TupleSet(0, 0).intern(), StructSet { a: 0, b: 0 }.intern());
523 assert_ne!(
524 EnumSet::Tuple(0, 0).intern(),
525 EnumSet::Struct { a: 0, b: 0 }.intern()
526 );
527
528 assert_eq!(
529 StructSet { a: 0, b: 0 }.intern(),
530 StructSet { a: 0, b: 0 }.intern()
531 );
532 assert_eq!(
533 EnumSet::Struct { a: 0, b: 0 }.intern(),
534 EnumSet::Struct { a: 0, b: 0 }.intern()
535 );
536 assert_ne!(
537 StructSet { a: 0, b: 0 }.intern(),
538 StructSet { a: 0, b: 1 }.intern()
539 );
540 assert_ne!(
541 EnumSet::Struct { a: 0, b: 0 }.intern(),
542 EnumSet::Struct { a: 0, b: 1 }.intern()
543 );
544 assert_ne!(
545 StructSet { a: 0, b: 0 }.intern(),
546 EnumSet::Struct { a: 0, b: 0 }.intern()
547 );
548 assert_ne!(
549 StructSet { a: 0, b: 0 }.intern(),
550 EnumSet::Struct { a: 0, b: 0 }.intern()
551 );
552 assert_ne!(StructSet { a: 0, b: 0 }.intern(), UnitSet.intern(),);
553 assert_ne!(
554 EnumSet::Struct { a: 0, b: 0 }.intern(),
555 EnumSet::Unit.intern()
556 );
557
558 assert_eq!(
559 GenericSet::<u32>(PhantomData).intern(),
560 GenericSet::<u32>(PhantomData).intern()
561 );
562 assert_ne!(
563 GenericSet::<u32>(PhantomData).intern(),
564 GenericSet::<u64>(PhantomData).intern()
565 );
566 }
567
568 #[test]
569 fn system_set_matches_default_system_set() {
570 fn system() {}
571 let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
572 let system = IntoSystem::into_system(system);
573 let set_from_system = system.default_system_sets()[0];
574 assert_eq!(set_from_into_system_set, set_from_system);
575 }
576
577 #[test]
578 fn system_set_matches_default_system_set_exclusive() {
579 fn system(_: &mut crate::world::World) {}
580 let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
581 let system = IntoSystem::into_system(system);
582 let set_from_system = system.default_system_sets()[0];
583 assert_eq!(set_from_into_system_set, set_from_system);
584 }
585}