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}