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