bevy/crates/bevy_ecs/src/schedule/set.rs
JoJoJet 85c8fb9dfa Revise docs for system set marker traits (#7882)
# Objective

#7863 introduced a potential footgun. When trying to incorrectly add a user-defined type using `in_base_set`, the compiler will suggest that the user implement `BaseSystemSet` for their type. This is a reasonable-sounding suggestion, however this is not the correct way to make a base set, and will lead to a confusing panic message when a marker trait is implemented for the wrong type.

## Solution

Rewrite the documentation for these traits, making it more clear that `BaseSystemSet` is a marker for types that are already base sets, and not a way to define a base set.
2023-03-03 14:43:54 +00:00

171 lines
4.5 KiB
Rust

use std::any::TypeId;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
pub use bevy_ecs_macros::{ScheduleLabel, SystemSet};
use bevy_utils::define_boxed_label;
use bevy_utils::label::DynHash;
use crate::system::{
ExclusiveSystemParamFunction, IsExclusiveFunctionSystem, IsFunctionSystem, SystemParamFunction,
};
define_boxed_label!(ScheduleLabel);
pub type BoxedSystemSet = Box<dyn SystemSet>;
pub type BoxedScheduleLabel = Box<dyn ScheduleLabel>;
/// Types that identify logical groups of systems.
pub trait SystemSet: DynHash + Debug + Send + Sync + 'static {
/// Returns `Some` if this system set is a [`SystemTypeSet`].
fn system_type(&self) -> Option<TypeId> {
None
}
/// Returns `true` if this set is a "base system set". Systems
/// can only belong to one base set at a time. Systems and Sets
/// can only be added to base sets using specialized `in_base_set`
/// APIs. This enables "mutually exclusive" behaviors. It also
/// enables schedules to have a "default base set", which can be used
/// to apply default configuration to systems.
fn is_base(&self) -> bool {
false
}
/// Creates a boxed clone of the label corresponding to this system set.
fn dyn_clone(&self) -> Box<dyn SystemSet>;
}
/// A marker trait for `SystemSet` types where [`is_base`] returns `true`.
/// This should only be implemented for types that satisfy this requirement.
/// It is automatically implemented for base set types by `#[derive(SystemSet)]`.
///
/// [`is_base`]: SystemSet::is_base
pub trait BaseSystemSet: SystemSet {}
/// A marker trait for `SystemSet` types where [`is_base`] returns `false`.
/// This should only be implemented for types that satisfy this requirement.
/// It is automatically implemented for non-base set types by `#[derive(SystemSet)]`.
///
/// [`is_base`]: SystemSet::is_base
pub trait FreeSystemSet: SystemSet {}
impl PartialEq for dyn SystemSet {
fn eq(&self, other: &Self) -> bool {
self.dyn_eq(other.as_dyn_eq())
}
}
impl Eq for dyn SystemSet {}
impl Hash for dyn SystemSet {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}
impl Clone for Box<dyn SystemSet> {
fn clone(&self) -> Self {
self.dyn_clone()
}
}
/// A [`SystemSet`] grouping instances of the same function.
///
/// This kind of set is automatically populated and thus has some special rules:
/// - You cannot manually add members.
/// - You cannot configure them.
/// - You cannot order something relative to one if it has more than one member.
pub struct SystemTypeSet<T: 'static>(PhantomData<fn() -> T>);
impl<T: 'static> SystemTypeSet<T> {
pub(crate) fn new() -> Self {
Self(PhantomData)
}
}
impl<T> Debug for SystemTypeSet<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SystemTypeSet")
.field(&std::any::type_name::<T>())
.finish()
}
}
impl<T> Hash for SystemTypeSet<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {
// all systems of a given type are the same
}
}
impl<T> Clone for SystemTypeSet<T> {
fn clone(&self) -> Self {
Self(PhantomData)
}
}
impl<T> Copy for SystemTypeSet<T> {}
impl<T> PartialEq for SystemTypeSet<T> {
#[inline]
fn eq(&self, _other: &Self) -> bool {
// all systems of a given type are the same
true
}
}
impl<T> Eq for SystemTypeSet<T> {}
impl<T> SystemSet for SystemTypeSet<T> {
fn system_type(&self) -> Option<TypeId> {
Some(TypeId::of::<T>())
}
fn dyn_clone(&self) -> Box<dyn SystemSet> {
Box::new(*self)
}
}
/// Types that can be converted into a [`SystemSet`].
pub trait IntoSystemSet<Marker>: Sized {
type Set: SystemSet;
fn into_system_set(self) -> Self::Set;
}
// systems sets
impl<S: SystemSet> IntoSystemSet<()> for S {
type Set = Self;
#[inline]
fn into_system_set(self) -> Self::Set {
self
}
}
// systems
impl<Marker, F> IntoSystemSet<(IsFunctionSystem, Marker)> for F
where
F: SystemParamFunction<Marker>,
{
type Set = SystemTypeSet<Self>;
#[inline]
fn into_system_set(self) -> Self::Set {
SystemTypeSet::new()
}
}
// exclusive systems
impl<Marker, F> IntoSystemSet<(IsExclusiveFunctionSystem, Marker)> for F
where
F: ExclusiveSystemParamFunction<Marker>,
{
type Set = SystemTypeSet<Self>;
#[inline]
fn into_system_set(self) -> Self::Set {
SystemTypeSet::new()
}
}