Simplify design for *Labels (#4957)

# Objective

- Closes #4954 
- Reduce the complexity of the `{System, App, *}Label` APIs.

## Solution

For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.

- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.

## Changelog

- String literals implement `SystemLabel` for now, but this should be changed with #4409 .

## Migration Guide

- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
    - No more output generics.
    - Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.

## Questions for later

* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
    * Empty string for unit structs -- no debug info but faster comparisons
    * Don't show enum types -- same tradeoffs as asbove.
This commit is contained in:
JoJoJet 2022-07-14 18:23:01 +00:00
parent 234e5af882
commit c43295af80
15 changed files with 309 additions and 318 deletions

View file

@ -63,11 +63,18 @@ pub fn build_schedule(criterion: &mut Criterion) {
// Use multiple different kinds of label to ensure that dynamic dispatch // Use multiple different kinds of label to ensure that dynamic dispatch
// doesn't somehow get optimized away. // doesn't somehow get optimized away.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)] #[derive(Debug, Clone, Copy)]
struct NumLabel(usize); struct NumLabel(usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)] #[derive(Debug, Clone, Copy, SystemLabel)]
struct DummyLabel; struct DummyLabel;
impl SystemLabel for NumLabel {
fn as_str(&self) -> &'static str {
let s = self.0.to_string();
Box::leak(s.into_boxed_str())
}
}
let mut group = criterion.benchmark_group("build_schedule"); let mut group = criterion.benchmark_group("build_schedule");
group.warm_up_time(std::time::Duration::from_millis(500)); group.warm_up_time(std::time::Duration::from_millis(500));
group.measurement_time(std::time::Duration::from_secs(15)); group.measurement_time(std::time::Duration::from_secs(15));
@ -75,7 +82,10 @@ pub fn build_schedule(criterion: &mut Criterion) {
// Method: generate a set of `graph_size` systems which have a One True Ordering. // Method: generate a set of `graph_size` systems which have a One True Ordering.
// Add system to the stage with full constraints. Hopefully this should be maximimally // Add system to the stage with full constraints. Hopefully this should be maximimally
// difficult for bevy to figure out. // difficult for bevy to figure out.
let labels: Vec<_> = (0..1000).map(NumLabel).collect(); // Also, we are performing the `as_label` operation outside of the loop since that
// requires an allocation and a leak. This is not something that would be necessary in a
// real scenario, just a contrivance for the benchmark.
let labels: Vec<_> = (0..1000).map(|i| NumLabel(i).as_label()).collect();
// Benchmark graphs of different sizes. // Benchmark graphs of different sizes.
for graph_size in [100, 500, 1000] { for graph_size in [100, 500, 1000] {

View file

@ -56,7 +56,7 @@ pub struct App {
pub runner: Box<dyn Fn(App)>, pub runner: Box<dyn Fn(App)>,
/// A container of [`Stage`]s set to be run in a linear order. /// A container of [`Stage`]s set to be run in a linear order.
pub schedule: Schedule, pub schedule: Schedule,
sub_apps: HashMap<Box<dyn AppLabel>, SubApp>, sub_apps: HashMap<AppLabelId, SubApp>,
} }
/// Each `SubApp` has its own [`Schedule`] and [`World`], enabling a separation of concerns. /// Each `SubApp` has its own [`Schedule`] and [`World`], enabling a separation of concerns.
@ -879,7 +879,7 @@ impl App {
sub_app_runner: impl Fn(&mut World, &mut App) + 'static, sub_app_runner: impl Fn(&mut World, &mut App) + 'static,
) -> &mut Self { ) -> &mut Self {
self.sub_apps.insert( self.sub_apps.insert(
Box::new(label), label.as_label(),
SubApp { SubApp {
app, app,
runner: Box::new(sub_app_runner), runner: Box::new(sub_app_runner),
@ -896,15 +896,16 @@ impl App {
pub fn sub_app_mut(&mut self, label: impl AppLabel) -> &mut App { pub fn sub_app_mut(&mut self, label: impl AppLabel) -> &mut App {
match self.get_sub_app_mut(label) { match self.get_sub_app_mut(label) {
Ok(app) => app, Ok(app) => app,
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label), Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
} }
} }
/// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns /// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
/// an [`Err`] containing the given label. /// an [`Err`] containing the given label.
pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, impl AppLabel> { pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, AppLabelId> {
let label = label.as_label();
self.sub_apps self.sub_apps
.get_mut((&label) as &dyn AppLabel) .get_mut(&label)
.map(|sub_app| &mut sub_app.app) .map(|sub_app| &mut sub_app.app)
.ok_or(label) .ok_or(label)
} }
@ -917,7 +918,7 @@ impl App {
pub fn sub_app(&self, label: impl AppLabel) -> &App { pub fn sub_app(&self, label: impl AppLabel) -> &App {
match self.get_sub_app(label) { match self.get_sub_app(label) {
Ok(app) => app, Ok(app) => app,
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label), Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
} }
} }
@ -925,7 +926,7 @@ impl App {
/// an [`Err`] containing the given label. /// an [`Err`] containing the given label.
pub fn get_sub_app(&self, label: impl AppLabel) -> Result<&App, impl AppLabel> { pub fn get_sub_app(&self, label: impl AppLabel) -> Result<&App, impl AppLabel> {
self.sub_apps self.sub_apps
.get((&label) as &dyn AppLabel) .get(&label.as_label())
.map(|sub_app| &sub_app.app) .map(|sub_app| &sub_app.app)
.ok_or(label) .ok_or(label)
} }

View file

@ -5,8 +5,3 @@ define_label!(StageLabel);
define_label!(SystemLabel); define_label!(SystemLabel);
define_label!(AmbiguitySetLabel); define_label!(AmbiguitySetLabel);
define_label!(RunCriteriaLabel); define_label!(RunCriteriaLabel);
pub(crate) type BoxedStageLabel = Box<dyn StageLabel>;
pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>;
pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>;
pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>;

View file

@ -38,8 +38,8 @@ use bevy_utils::HashMap;
/// runs indefinitely. /// runs indefinitely.
#[derive(Default)] #[derive(Default)]
pub struct Schedule { pub struct Schedule {
stages: HashMap<BoxedStageLabel, Box<dyn Stage>>, stages: HashMap<StageLabelId, Box<dyn Stage>>,
stage_order: Vec<BoxedStageLabel>, stage_order: Vec<StageLabelId>,
run_criteria: BoxedRunCriteria, run_criteria: BoxedRunCriteria,
} }
@ -109,9 +109,9 @@ impl Schedule {
/// schedule.add_stage("my_stage", SystemStage::parallel()); /// schedule.add_stage("my_stage", SystemStage::parallel());
/// ``` /// ```
pub fn add_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self { pub fn add_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self {
let label: Box<dyn StageLabel> = Box::new(label); let label = label.as_label();
self.stage_order.push(label.clone()); self.stage_order.push(label);
let prev = self.stages.insert(label.clone(), Box::new(stage)); let prev = self.stages.insert(label, Box::new(stage));
assert!(prev.is_none(), "Stage already exists: {:?}.", label); assert!(prev.is_none(), "Stage already exists: {:?}.", label);
self self
} }
@ -133,18 +133,18 @@ impl Schedule {
label: impl StageLabel, label: impl StageLabel,
stage: S, stage: S,
) -> &mut Self { ) -> &mut Self {
let label: Box<dyn StageLabel> = Box::new(label); let label = label.as_label();
let target = &target as &dyn StageLabel; let target = target.as_label();
let target_index = self let target_index = self
.stage_order .stage_order
.iter() .iter()
.enumerate() .enumerate()
.find(|(_i, stage_label)| &***stage_label == target) .find(|(_i, stage_label)| **stage_label == target)
.map(|(i, _)| i) .map(|(i, _)| i)
.unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target)); .unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target));
self.stage_order.insert(target_index + 1, label.clone()); self.stage_order.insert(target_index + 1, label);
let prev = self.stages.insert(label.clone(), Box::new(stage)); let prev = self.stages.insert(label, Box::new(stage));
assert!(prev.is_none(), "Stage already exists: {:?}.", label); assert!(prev.is_none(), "Stage already exists: {:?}.", label);
self self
} }
@ -167,18 +167,18 @@ impl Schedule {
label: impl StageLabel, label: impl StageLabel,
stage: S, stage: S,
) -> &mut Self { ) -> &mut Self {
let label: Box<dyn StageLabel> = Box::new(label); let label = label.as_label();
let target = &target as &dyn StageLabel; let target = target.as_label();
let target_index = self let target_index = self
.stage_order .stage_order
.iter() .iter()
.enumerate() .enumerate()
.find(|(_i, stage_label)| &***stage_label == target) .find(|(_i, stage_label)| **stage_label == target)
.map(|(i, _)| i) .map(|(i, _)| i)
.unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target)); .unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target));
self.stage_order.insert(target_index, label.clone()); self.stage_order.insert(target_index, label);
let prev = self.stages.insert(label.clone(), Box::new(stage)); let prev = self.stages.insert(label, Box::new(stage));
assert!(prev.is_none(), "Stage already exists: {:?}.", label); assert!(prev.is_none(), "Stage already exists: {:?}.", label);
self self
} }
@ -213,7 +213,7 @@ impl Schedule {
let stage = self let stage = self
.get_stage_mut::<SystemStage>(&stage_label) .get_stage_mut::<SystemStage>(&stage_label)
.unwrap_or_else(move || stage_not_found(&stage_label)); .unwrap_or_else(move || stage_not_found(&stage_label.as_label()));
stage.add_system(system); stage.add_system(system);
self self
} }
@ -282,7 +282,10 @@ impl Schedule {
func: F, func: F,
) -> &mut Self { ) -> &mut Self {
let stage = self.get_stage_mut::<T>(&label).unwrap_or_else(move || { let stage = self.get_stage_mut::<T>(&label).unwrap_or_else(move || {
panic!("stage '{:?}' does not exist or is the wrong type", label) panic!(
"stage '{:?}' does not exist or is the wrong type",
label.as_label()
)
}); });
func(stage); func(stage);
self self
@ -305,7 +308,7 @@ impl Schedule {
/// ``` /// ```
pub fn get_stage<T: Stage>(&self, label: &dyn StageLabel) -> Option<&T> { pub fn get_stage<T: Stage>(&self, label: &dyn StageLabel) -> Option<&T> {
self.stages self.stages
.get(label) .get(&label.as_label())
.and_then(|stage| stage.downcast_ref::<T>()) .and_then(|stage| stage.downcast_ref::<T>())
} }
@ -326,7 +329,7 @@ impl Schedule {
/// ``` /// ```
pub fn get_stage_mut<T: Stage>(&mut self, label: &dyn StageLabel) -> Option<&mut T> { pub fn get_stage_mut<T: Stage>(&mut self, label: &dyn StageLabel) -> Option<&mut T> {
self.stages self.stages
.get_mut(label) .get_mut(&label.as_label())
.and_then(|stage| stage.downcast_mut::<T>()) .and_then(|stage| stage.downcast_mut::<T>())
} }
@ -341,10 +344,10 @@ impl Schedule {
} }
/// Iterates over all of schedule's stages and their labels, in execution order. /// Iterates over all of schedule's stages and their labels, in execution order.
pub fn iter_stages(&self) -> impl Iterator<Item = (&dyn StageLabel, &dyn Stage)> { pub fn iter_stages(&self) -> impl Iterator<Item = (StageLabelId, &dyn Stage)> {
self.stage_order self.stage_order
.iter() .iter()
.map(move |label| (&**label, &*self.stages[label])) .map(move |&label| (label, &*self.stages[&label]))
} }
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel}, schedule::{GraphNode, RunCriteriaLabel, RunCriteriaLabelId},
system::{BoxedSystem, IntoSystem, Local}, system::{BoxedSystem, IntoSystem, Local},
world::World, world::World,
}; };
@ -104,9 +104,9 @@ pub(crate) enum RunCriteriaInner {
pub(crate) struct RunCriteriaContainer { pub(crate) struct RunCriteriaContainer {
pub(crate) should_run: ShouldRun, pub(crate) should_run: ShouldRun,
pub(crate) inner: RunCriteriaInner, pub(crate) inner: RunCriteriaInner,
pub(crate) label: Option<BoxedRunCriteriaLabel>, pub(crate) label: Option<RunCriteriaLabelId>,
pub(crate) before: Vec<BoxedRunCriteriaLabel>, pub(crate) before: Vec<RunCriteriaLabelId>,
pub(crate) after: Vec<BoxedRunCriteriaLabel>, pub(crate) after: Vec<RunCriteriaLabelId>,
} }
impl RunCriteriaContainer { impl RunCriteriaContainer {
@ -139,7 +139,7 @@ impl RunCriteriaContainer {
} }
impl GraphNode for RunCriteriaContainer { impl GraphNode for RunCriteriaContainer {
type Label = BoxedRunCriteriaLabel; type Label = RunCriteriaLabelId;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> Cow<'static, str> {
match &self.inner { match &self.inner {
@ -148,7 +148,7 @@ impl GraphNode for RunCriteriaContainer {
} }
} }
fn labels(&self) -> &[BoxedRunCriteriaLabel] { fn labels(&self) -> &[RunCriteriaLabelId] {
if let Some(ref label) = self.label { if let Some(ref label) = self.label {
std::slice::from_ref(label) std::slice::from_ref(label)
} else { } else {
@ -156,18 +156,18 @@ impl GraphNode for RunCriteriaContainer {
} }
} }
fn before(&self) -> &[BoxedRunCriteriaLabel] { fn before(&self) -> &[RunCriteriaLabelId] {
&self.before &self.before
} }
fn after(&self) -> &[BoxedRunCriteriaLabel] { fn after(&self) -> &[RunCriteriaLabelId] {
&self.after &self.after
} }
} }
pub enum RunCriteriaDescriptorOrLabel { pub enum RunCriteriaDescriptorOrLabel {
Descriptor(RunCriteriaDescriptor), Descriptor(RunCriteriaDescriptor),
Label(BoxedRunCriteriaLabel), Label(RunCriteriaLabelId),
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -178,10 +178,10 @@ pub(crate) enum DuplicateLabelStrategy {
pub struct RunCriteriaDescriptor { pub struct RunCriteriaDescriptor {
pub(crate) system: RunCriteriaSystem, pub(crate) system: RunCriteriaSystem,
pub(crate) label: Option<BoxedRunCriteriaLabel>, pub(crate) label: Option<RunCriteriaLabelId>,
pub(crate) duplicate_label_strategy: DuplicateLabelStrategy, pub(crate) duplicate_label_strategy: DuplicateLabelStrategy,
pub(crate) before: Vec<BoxedRunCriteriaLabel>, pub(crate) before: Vec<RunCriteriaLabelId>,
pub(crate) after: Vec<BoxedRunCriteriaLabel>, pub(crate) after: Vec<RunCriteriaLabelId>,
} }
pub(crate) enum RunCriteriaSystem { pub(crate) enum RunCriteriaSystem {
@ -222,12 +222,12 @@ where
} }
} }
impl<L> IntoRunCriteria<BoxedRunCriteriaLabel> for L impl<L> IntoRunCriteria<RunCriteriaLabelId> for L
where where
L: RunCriteriaLabel, L: RunCriteriaLabel,
{ {
fn into(self) -> RunCriteriaDescriptorOrLabel { fn into(self) -> RunCriteriaDescriptorOrLabel {
RunCriteriaDescriptorOrLabel::Label(Box::new(self)) RunCriteriaDescriptorOrLabel::Label(self.as_label())
} }
} }
@ -254,24 +254,24 @@ pub trait RunCriteriaDescriptorCoercion<Param> {
impl RunCriteriaDescriptorCoercion<()> for RunCriteriaDescriptor { impl RunCriteriaDescriptorCoercion<()> for RunCriteriaDescriptor {
fn label(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor { fn label(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.label = Some(Box::new(label)); self.label = Some(label.as_label());
self.duplicate_label_strategy = DuplicateLabelStrategy::Panic; self.duplicate_label_strategy = DuplicateLabelStrategy::Panic;
self self
} }
fn label_discard_if_duplicate(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor { fn label_discard_if_duplicate(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.label = Some(Box::new(label)); self.label = Some(label.as_label());
self.duplicate_label_strategy = DuplicateLabelStrategy::Discard; self.duplicate_label_strategy = DuplicateLabelStrategy::Discard;
self self
} }
fn before(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor { fn before(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.before.push(Box::new(label)); self.before.push(label.as_label());
self self
} }
fn after(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor { fn after(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.after.push(Box::new(label)); self.after.push(label.as_label());
self self
} }
} }
@ -327,7 +327,7 @@ where
} }
pub struct RunCriteria { pub struct RunCriteria {
label: BoxedRunCriteriaLabel, label: RunCriteriaLabelId,
} }
impl RunCriteria { impl RunCriteria {
@ -342,7 +342,7 @@ impl RunCriteria {
label: None, label: None,
duplicate_label_strategy: DuplicateLabelStrategy::Panic, duplicate_label_strategy: DuplicateLabelStrategy::Panic,
before: vec![], before: vec![],
after: vec![Box::new(label)], after: vec![label.as_label()],
} }
} }
} }

View file

@ -4,11 +4,11 @@ use crate::{
prelude::IntoSystem, prelude::IntoSystem,
schedule::{ schedule::{
graph_utils::{self, DependencyGraphError}, graph_utils::{self, DependencyGraphError},
BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy, BoxedRunCriteria, DuplicateLabelStrategy, ExclusiveSystemContainer, GraphNode,
ExclusiveSystemContainer, GraphNode, InsertionPoint, ParallelExecutor, InsertionPoint, ParallelExecutor, ParallelSystemContainer, ParallelSystemExecutor,
ParallelSystemContainer, ParallelSystemExecutor, RunCriteriaContainer, RunCriteriaContainer, RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel,
RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun, RunCriteriaInner, RunCriteriaLabelId, ShouldRun, SingleThreadedExecutor, SystemContainer,
SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet, SystemDescriptor, SystemLabelId, SystemSet,
}, },
world::{World, WorldId}, world::{World, WorldId},
}; };
@ -171,7 +171,7 @@ impl SystemStage {
container.run_criteria_label = Some(label); container.run_criteria_label = Some(label);
} }
Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => { Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => {
container.run_criteria_label = criteria_descriptor.label.clone(); container.run_criteria_label = criteria_descriptor.label;
container.run_criteria_index = container.run_criteria_index =
Some(self.add_run_criteria_internal(criteria_descriptor)); Some(self.add_run_criteria_internal(criteria_descriptor));
} }
@ -205,7 +205,7 @@ impl SystemStage {
container.run_criteria_label = Some(label); container.run_criteria_label = Some(label);
} }
Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => { Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => {
container.run_criteria_label = criteria_descriptor.label.clone(); container.run_criteria_label = criteria_descriptor.label;
container.run_criteria_index = container.run_criteria_index =
Some(self.add_run_criteria_internal(criteria_descriptor)); Some(self.add_run_criteria_internal(criteria_descriptor));
} }
@ -301,11 +301,11 @@ impl SystemStage {
match system { match system {
SystemDescriptor::Exclusive(descriptor) => { SystemDescriptor::Exclusive(descriptor) => {
descriptor.run_criteria = descriptor.run_criteria =
Some(RunCriteriaDescriptorOrLabel::Label(label.clone())); Some(RunCriteriaDescriptorOrLabel::Label(label));
} }
SystemDescriptor::Parallel(descriptor) => { SystemDescriptor::Parallel(descriptor) => {
descriptor.run_criteria = descriptor.run_criteria =
Some(RunCriteriaDescriptorOrLabel::Label(label.clone())); Some(RunCriteriaDescriptorOrLabel::Label(label));
} }
} }
} }
@ -372,7 +372,7 @@ impl SystemStage {
.enumerate() .enumerate()
.filter_map(|(index, mut container)| { .filter_map(|(index, mut container)| {
let new_index = index - filtered_criteria; let new_index = index - filtered_criteria;
let label = container.label.clone(); let label = container.label;
if let Some(strategy) = uninitialized_criteria.get(&index) { if let Some(strategy) = uninitialized_criteria.get(&index) {
if let Some(ref label) = label { if let Some(ref label) = label {
if let Some(duplicate_index) = criteria_labels.get(label) { if let Some(duplicate_index) = criteria_labels.get(label) {
@ -621,10 +621,8 @@ impl SystemStage {
/// Returns a map of run criteria labels to their indices. /// Returns a map of run criteria labels to their indices.
fn process_run_criteria( fn process_run_criteria(
&mut self, &mut self,
) -> Result< ) -> Result<HashMap<RunCriteriaLabelId, usize>, DependencyGraphError<HashSet<RunCriteriaLabelId>>>
HashMap<BoxedRunCriteriaLabel, usize>, {
DependencyGraphError<HashSet<BoxedRunCriteriaLabel>>,
> {
let graph = graph_utils::build_dependency_graph(&self.run_criteria); let graph = graph_utils::build_dependency_graph(&self.run_criteria);
let order = graph_utils::topological_order(&graph)?; let order = graph_utils::topological_order(&graph)?;
let mut order_inverted = order.iter().enumerate().collect::<Vec<_>>(); let mut order_inverted = order.iter().enumerate().collect::<Vec<_>>();
@ -637,7 +635,7 @@ impl SystemStage {
criteria criteria
.label .label
.as_ref() .as_ref()
.map(|label| (label.clone(), order_inverted[index].0)) .map(|&label| (label, order_inverted[index].0))
}) })
.collect(); .collect();
for criteria in &mut self.run_criteria { for criteria in &mut self.run_criteria {
@ -680,8 +678,8 @@ impl SystemStage {
/// and run criteria. /// and run criteria.
fn process_systems( fn process_systems(
systems: &mut Vec<impl SystemContainer>, systems: &mut Vec<impl SystemContainer>,
run_criteria_labels: &HashMap<BoxedRunCriteriaLabel, usize>, run_criteria_labels: &HashMap<RunCriteriaLabelId, usize>,
) -> Result<(), DependencyGraphError<HashSet<BoxedSystemLabel>>> { ) -> Result<(), DependencyGraphError<HashSet<SystemLabelId>>> {
let mut graph = graph_utils::build_dependency_graph(systems); let mut graph = graph_utils::build_dependency_graph(systems);
let order = graph_utils::topological_order(&graph)?; let order = graph_utils::topological_order(&graph)?;
let mut order_inverted = order.iter().enumerate().collect::<Vec<_>>(); let mut order_inverted = order.iter().enumerate().collect::<Vec<_>>();
@ -974,9 +972,9 @@ impl Stage for SystemStage {
mod tests { mod tests {
use crate::{ use crate::{
schedule::{ schedule::{
BoxedSystemLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria,
RunCriteria, RunCriteriaDescriptorCoercion, ShouldRun, SingleThreadedExecutor, Stage, RunCriteriaDescriptorCoercion, ShouldRun, SingleThreadedExecutor, Stage, SystemLabel,
SystemSet, SystemStage, SystemLabelId, SystemSet, SystemStage,
}, },
system::{In, IntoExclusiveSystem, Local, Query, ResMut}, system::{In, IntoExclusiveSystem, Local, Query, ResMut},
world::World, world::World,
@ -1609,23 +1607,21 @@ mod tests {
fn find_ambiguities_first_str_labels( fn find_ambiguities_first_str_labels(
systems: &[impl SystemContainer], systems: &[impl SystemContainer],
) -> Vec<(BoxedSystemLabel, BoxedSystemLabel)> { ) -> Vec<(SystemLabelId, SystemLabelId)> {
find_ambiguities(systems) find_ambiguities(systems)
.drain(..) .drain(..)
.map(|(index_a, index_b, _conflicts)| { .map(|(index_a, index_b, _conflicts)| {
( (
systems[index_a] *systems[index_a]
.labels() .labels()
.iter() .iter()
.find(|a| (&***a).type_id() == std::any::TypeId::of::<&str>()) .find(|a| a.type_id() == std::any::TypeId::of::<&str>())
.unwrap() .unwrap(),
.clone(), *systems[index_b]
systems[index_b]
.labels() .labels()
.iter() .iter()
.find(|a| (&***a).type_id() == std::any::TypeId::of::<&str>()) .find(|a| a.type_id() == std::any::TypeId::of::<&str>())
.unwrap() .unwrap(),
.clone(),
) )
}) })
.collect() .collect()
@ -1657,8 +1653,8 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4"))) ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("1"))) || ambiguities.contains(&("4".as_label(), "1".as_label()))
); );
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
@ -1672,8 +1668,8 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4"))) ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("1"))) || ambiguities.contains(&("4".as_label(), "1".as_label()))
); );
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
@ -1697,12 +1693,12 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("0"), Box::new("3"))) ambiguities.contains(&("0".as_label(), "3".as_label()))
|| ambiguities.contains(&(Box::new("3"), Box::new("0"))) || ambiguities.contains(&("3".as_label(), "0".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4"))) ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("1"))) || ambiguities.contains(&("4".as_label(), "1".as_label()))
); );
assert_eq!(ambiguities.len(), 2); assert_eq!(ambiguities.len(), 2);
@ -1716,8 +1712,8 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("0"), Box::new("3"))) ambiguities.contains(&("0".as_label(), "3".as_label()))
|| ambiguities.contains(&(Box::new("3"), Box::new("0"))) || ambiguities.contains(&("3".as_label(), "0".as_label()))
); );
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
@ -1729,8 +1725,8 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("0"), Box::new("1"))) ambiguities.contains(&("0".as_label(), "1".as_label()))
|| ambiguities.contains(&(Box::new("1"), Box::new("0"))) || ambiguities.contains(&("1".as_label(), "0".as_label()))
); );
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
@ -1742,8 +1738,8 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2"))) ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&(Box::new("2"), Box::new("1"))) || ambiguities.contains(&("2".as_label(), "1".as_label()))
); );
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
@ -1756,8 +1752,8 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2"))) ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&(Box::new("2"), Box::new("1"))) || ambiguities.contains(&("2".as_label(), "1".as_label()))
); );
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
@ -1780,8 +1776,8 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2"))) ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&(Box::new("2"), Box::new("1"))) || ambiguities.contains(&("2".as_label(), "1".as_label()))
); );
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
@ -1810,28 +1806,28 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2"))) ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&(Box::new("2"), Box::new("1"))) || ambiguities.contains(&("2".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("3"))) ambiguities.contains(&("1".as_label(), "3".as_label()))
|| ambiguities.contains(&(Box::new("3"), Box::new("1"))) || ambiguities.contains(&("3".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4"))) ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("1"))) || ambiguities.contains(&("4".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("3"))) ambiguities.contains(&("2".as_label(), "3".as_label()))
|| ambiguities.contains(&(Box::new("3"), Box::new("2"))) || ambiguities.contains(&("3".as_label(), "2".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4"))) ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("2"))) || ambiguities.contains(&("4".as_label(), "2".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("3"), Box::new("4"))) ambiguities.contains(&("3".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("3"))) || ambiguities.contains(&("4".as_label(), "3".as_label()))
); );
assert_eq!(ambiguities.len(), 6); assert_eq!(ambiguities.len(), 6);
@ -1891,12 +1887,12 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel); let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4"))) ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("1"))) || ambiguities.contains(&("4".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4"))) ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("2"))) || ambiguities.contains(&("4".as_label(), "2".as_label()))
); );
assert_eq!(ambiguities.len(), 2); assert_eq!(ambiguities.len(), 2);
@ -1925,28 +1921,28 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.exclusive_at_start); let ambiguities = find_ambiguities_first_str_labels(&stage.exclusive_at_start);
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("3"))) ambiguities.contains(&("1".as_label(), "3".as_label()))
|| ambiguities.contains(&(Box::new("3"), Box::new("1"))) || ambiguities.contains(&("3".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("3"))) ambiguities.contains(&("2".as_label(), "3".as_label()))
|| ambiguities.contains(&(Box::new("3"), Box::new("2"))) || ambiguities.contains(&("3".as_label(), "2".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4"))) ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("1"))) || ambiguities.contains(&("4".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4"))) ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("2"))) || ambiguities.contains(&("4".as_label(), "2".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("5"))) ambiguities.contains(&("1".as_label(), "5".as_label()))
|| ambiguities.contains(&(Box::new("5"), Box::new("1"))) || ambiguities.contains(&("5".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("5"))) ambiguities.contains(&("2".as_label(), "5".as_label()))
|| ambiguities.contains(&(Box::new("5"), Box::new("2"))) || ambiguities.contains(&("5".as_label(), "2".as_label()))
); );
assert_eq!(ambiguities.len(), 6); assert_eq!(ambiguities.len(), 6);
@ -1962,20 +1958,20 @@ mod tests {
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.exclusive_at_start); let ambiguities = find_ambiguities_first_str_labels(&stage.exclusive_at_start);
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("3"))) ambiguities.contains(&("2".as_label(), "3".as_label()))
|| ambiguities.contains(&(Box::new("3"), Box::new("2"))) || ambiguities.contains(&("3".as_label(), "2".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4"))) ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("1"))) || ambiguities.contains(&("4".as_label(), "1".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4"))) ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&(Box::new("4"), Box::new("2"))) || ambiguities.contains(&("4".as_label(), "2".as_label()))
); );
assert!( assert!(
ambiguities.contains(&(Box::new("2"), Box::new("5"))) ambiguities.contains(&("2".as_label(), "5".as_label()))
|| ambiguities.contains(&(Box::new("5"), Box::new("2"))) || ambiguities.contains(&("5".as_label(), "2".as_label()))
); );
assert_eq!(ambiguities.len(), 4); assert_eq!(ambiguities.len(), 4);

View file

@ -53,47 +53,19 @@ enum ScheduledOperation<T: StateData> {
} }
#[derive(Debug, PartialEq, Eq, Clone, Hash)] #[derive(Debug, PartialEq, Eq, Clone, Hash)]
enum StateCallback { struct DriverLabel(TypeId, &'static str);
Update,
InactiveUpdate,
InStackUpdate,
Enter,
Exit,
Pause,
Resume,
}
impl StateCallback {
fn into_label<T>(self, state: T) -> StateRunCriteriaLabel<T>
where
T: StateData,
{
StateRunCriteriaLabel(state, self)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
struct StateRunCriteriaLabel<T>(T, StateCallback);
impl<T> RunCriteriaLabel for StateRunCriteriaLabel<T>
where
T: StateData,
{
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> {
Box::new(self.clone())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
struct DriverLabel(TypeId);
impl RunCriteriaLabel for DriverLabel { impl RunCriteriaLabel for DriverLabel {
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> { fn type_id(&self) -> core::any::TypeId {
Box::new(self.clone()) self.0
}
fn as_str(&self) -> &'static str {
self.1
} }
} }
impl DriverLabel { impl DriverLabel {
fn of<T: 'static>() -> Self { fn of<T: 'static>() -> Self {
Self(TypeId::of::<T>()) Self(TypeId::of::<T>(), std::any::type_name::<T>())
} }
} }
@ -102,17 +74,14 @@ where
T: StateData, T: StateData,
{ {
pub fn on_update(pred: T) -> RunCriteriaDescriptor { pub fn on_update(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| { (move |state: Res<State<T>>| {
state.stack.last().unwrap() == &pred && state.transition.is_none() state.stack.last().unwrap() == &pred && state.transition.is_none()
}) })
.chain(should_run_adapter::<T>) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Update.into_label(pred_clone))
} }
pub fn on_inactive_update(pred: T) -> RunCriteriaDescriptor { pub fn on_inactive_update(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>, mut is_inactive: Local<bool>| match &state.transition { (move |state: Res<State<T>>, mut is_inactive: Local<bool>| match &state.transition {
Some(StateTransition::Pausing(ref relevant, _)) Some(StateTransition::Pausing(ref relevant, _))
| Some(StateTransition::Resuming(_, ref relevant)) => { | Some(StateTransition::Resuming(_, ref relevant)) => {
@ -126,11 +95,9 @@ where
}) })
.chain(should_run_adapter::<T>) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InactiveUpdate.into_label(pred_clone))
} }
pub fn on_in_stack_update(pred: T) -> RunCriteriaDescriptor { pub fn on_in_stack_update(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>, mut is_in_stack: Local<bool>| match &state.transition { (move |state: Res<State<T>>, mut is_in_stack: Local<bool>| match &state.transition {
Some(StateTransition::Entering(ref relevant, _)) Some(StateTransition::Entering(ref relevant, _))
| Some(StateTransition::ExitingToResume(_, ref relevant)) | Some(StateTransition::ExitingToResume(_, ref relevant))
@ -151,11 +118,9 @@ where
}) })
.chain(should_run_adapter::<T>) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InStackUpdate.into_label(pred_clone))
} }
pub fn on_enter(pred: T) -> RunCriteriaDescriptor { pub fn on_enter(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| { (move |state: Res<State<T>>| {
state state
.transition .transition
@ -168,11 +133,9 @@ where
}) })
.chain(should_run_adapter::<T>) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Enter.into_label(pred_clone))
} }
pub fn on_exit(pred: T) -> RunCriteriaDescriptor { pub fn on_exit(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| { (move |state: Res<State<T>>| {
state state
.transition .transition
@ -185,11 +148,9 @@ where
}) })
.chain(should_run_adapter::<T>) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Exit.into_label(pred_clone))
} }
pub fn on_pause(pred: T) -> RunCriteriaDescriptor { pub fn on_pause(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| { (move |state: Res<State<T>>| {
state state
.transition .transition
@ -201,11 +162,9 @@ where
}) })
.chain(should_run_adapter::<T>) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Pause.into_label(pred_clone))
} }
pub fn on_resume(pred: T) -> RunCriteriaDescriptor { pub fn on_resume(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| { (move |state: Res<State<T>>| {
state state
.transition .transition
@ -217,7 +176,6 @@ where
}) })
.chain(should_run_adapter::<T>) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Resume.into_label(pred_clone))
} }
pub fn on_update_set(s: T) -> SystemSet { pub fn on_update_set(s: T) -> SystemSet {

View file

@ -2,15 +2,15 @@ use crate::{
component::ComponentId, component::ComponentId,
query::Access, query::Access,
schedule::{ schedule::{
BoxedAmbiguitySetLabel, BoxedRunCriteriaLabel, BoxedSystemLabel, ExclusiveSystemDescriptor, AmbiguitySetLabelId, ExclusiveSystemDescriptor, GraphNode, ParallelSystemDescriptor,
GraphNode, ParallelSystemDescriptor, RunCriteriaLabelId, SystemLabelId,
}, },
system::{ExclusiveSystem, System}, system::{ExclusiveSystem, System},
}; };
use std::borrow::Cow; use std::borrow::Cow;
/// System metadata like its name, labels, order requirements and component access. /// System metadata like its name, labels, order requirements and component access.
pub trait SystemContainer: GraphNode<Label = BoxedSystemLabel> { pub trait SystemContainer: GraphNode<Label = SystemLabelId> {
#[doc(hidden)] #[doc(hidden)]
fn dependencies(&self) -> &[usize]; fn dependencies(&self) -> &[usize];
#[doc(hidden)] #[doc(hidden)]
@ -19,20 +19,20 @@ pub trait SystemContainer: GraphNode<Label = BoxedSystemLabel> {
fn run_criteria(&self) -> Option<usize>; fn run_criteria(&self) -> Option<usize>;
#[doc(hidden)] #[doc(hidden)]
fn set_run_criteria(&mut self, index: usize); fn set_run_criteria(&mut self, index: usize);
fn run_criteria_label(&self) -> Option<&BoxedRunCriteriaLabel>; fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId>;
fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel]; fn ambiguity_sets(&self) -> &[AmbiguitySetLabelId];
fn component_access(&self) -> Option<&Access<ComponentId>>; fn component_access(&self) -> Option<&Access<ComponentId>>;
} }
pub(super) struct ExclusiveSystemContainer { pub(super) struct ExclusiveSystemContainer {
system: Box<dyn ExclusiveSystem>, system: Box<dyn ExclusiveSystem>,
pub(super) run_criteria_index: Option<usize>, pub(super) run_criteria_index: Option<usize>,
pub(super) run_criteria_label: Option<BoxedRunCriteriaLabel>, pub(super) run_criteria_label: Option<RunCriteriaLabelId>,
dependencies: Vec<usize>, dependencies: Vec<usize>,
labels: Vec<BoxedSystemLabel>, labels: Vec<SystemLabelId>,
before: Vec<BoxedSystemLabel>, before: Vec<SystemLabelId>,
after: Vec<BoxedSystemLabel>, after: Vec<SystemLabelId>,
ambiguity_sets: Vec<BoxedAmbiguitySetLabel>, ambiguity_sets: Vec<AmbiguitySetLabelId>,
} }
impl ExclusiveSystemContainer { impl ExclusiveSystemContainer {
@ -55,21 +55,21 @@ impl ExclusiveSystemContainer {
} }
impl GraphNode for ExclusiveSystemContainer { impl GraphNode for ExclusiveSystemContainer {
type Label = BoxedSystemLabel; type Label = SystemLabelId;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> Cow<'static, str> {
self.system.name() self.system.name()
} }
fn labels(&self) -> &[BoxedSystemLabel] { fn labels(&self) -> &[SystemLabelId] {
&self.labels &self.labels
} }
fn before(&self) -> &[BoxedSystemLabel] { fn before(&self) -> &[SystemLabelId] {
&self.before &self.before
} }
fn after(&self) -> &[BoxedSystemLabel] { fn after(&self) -> &[SystemLabelId] {
&self.after &self.after
} }
} }
@ -92,11 +92,11 @@ impl SystemContainer for ExclusiveSystemContainer {
self.run_criteria_index = Some(index); self.run_criteria_index = Some(index);
} }
fn run_criteria_label(&self) -> Option<&BoxedRunCriteriaLabel> { fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId> {
self.run_criteria_label.as_ref() self.run_criteria_label.as_ref()
} }
fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel] { fn ambiguity_sets(&self) -> &[AmbiguitySetLabelId] {
&self.ambiguity_sets &self.ambiguity_sets
} }
@ -108,13 +108,13 @@ impl SystemContainer for ExclusiveSystemContainer {
pub struct ParallelSystemContainer { pub struct ParallelSystemContainer {
system: Box<dyn System<In = (), Out = ()>>, system: Box<dyn System<In = (), Out = ()>>,
pub(crate) run_criteria_index: Option<usize>, pub(crate) run_criteria_index: Option<usize>,
pub(crate) run_criteria_label: Option<BoxedRunCriteriaLabel>, pub(crate) run_criteria_label: Option<RunCriteriaLabelId>,
pub(crate) should_run: bool, pub(crate) should_run: bool,
dependencies: Vec<usize>, dependencies: Vec<usize>,
labels: Vec<BoxedSystemLabel>, labels: Vec<SystemLabelId>,
before: Vec<BoxedSystemLabel>, before: Vec<SystemLabelId>,
after: Vec<BoxedSystemLabel>, after: Vec<SystemLabelId>,
ambiguity_sets: Vec<BoxedAmbiguitySetLabel>, ambiguity_sets: Vec<AmbiguitySetLabelId>,
} }
impl ParallelSystemContainer { impl ParallelSystemContainer {
@ -154,21 +154,21 @@ impl ParallelSystemContainer {
} }
impl GraphNode for ParallelSystemContainer { impl GraphNode for ParallelSystemContainer {
type Label = BoxedSystemLabel; type Label = SystemLabelId;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> Cow<'static, str> {
self.system().name() self.system().name()
} }
fn labels(&self) -> &[BoxedSystemLabel] { fn labels(&self) -> &[SystemLabelId] {
&self.labels &self.labels
} }
fn before(&self) -> &[BoxedSystemLabel] { fn before(&self) -> &[SystemLabelId] {
&self.before &self.before
} }
fn after(&self) -> &[BoxedSystemLabel] { fn after(&self) -> &[SystemLabelId] {
&self.after &self.after
} }
} }
@ -191,11 +191,11 @@ impl SystemContainer for ParallelSystemContainer {
self.run_criteria_index = Some(index); self.run_criteria_index = Some(index);
} }
fn run_criteria_label(&self) -> Option<&BoxedRunCriteriaLabel> { fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId> {
self.run_criteria_label.as_ref() self.run_criteria_label.as_ref()
} }
fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel] { fn ambiguity_sets(&self) -> &[AmbiguitySetLabelId] {
&self.ambiguity_sets &self.ambiguity_sets
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
schedule::{ schedule::{
AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria, AmbiguitySetLabel, AmbiguitySetLabelId, IntoRunCriteria, RunCriteriaDescriptorOrLabel,
RunCriteriaDescriptorOrLabel, SystemLabel, SystemLabel, SystemLabelId,
}, },
system::{ system::{
AsSystemLabel, BoxedSystem, ExclusiveSystem, ExclusiveSystemCoerced, ExclusiveSystemFn, AsSystemLabel, BoxedSystem, ExclusiveSystem, ExclusiveSystemCoerced, ExclusiveSystemFn,
@ -98,10 +98,10 @@ impl IntoSystemDescriptor<()> for ExclusiveSystemCoerced {
pub struct ParallelSystemDescriptor { pub struct ParallelSystemDescriptor {
pub(crate) system: BoxedSystem<(), ()>, pub(crate) system: BoxedSystem<(), ()>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>, pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<BoxedSystemLabel>, pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<BoxedSystemLabel>, pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<BoxedSystemLabel>, pub(crate) after: Vec<SystemLabelId>,
pub(crate) ambiguity_sets: Vec<BoxedAmbiguitySetLabel>, pub(crate) ambiguity_sets: Vec<AmbiguitySetLabelId>,
} }
fn new_parallel_descriptor(system: BoxedSystem<(), ()>) -> ParallelSystemDescriptor { fn new_parallel_descriptor(system: BoxedSystem<(), ()>) -> ParallelSystemDescriptor {
@ -147,22 +147,22 @@ impl ParallelSystemDescriptorCoercion<()> for ParallelSystemDescriptor {
} }
fn label(mut self, label: impl SystemLabel) -> ParallelSystemDescriptor { fn label(mut self, label: impl SystemLabel) -> ParallelSystemDescriptor {
self.labels.push(Box::new(label)); self.labels.push(label.as_label());
self self
} }
fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
self.before.push(Box::new(label.as_system_label())); self.before.push(label.as_system_label().as_label());
self self
} }
fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
self.after.push(Box::new(label.as_system_label())); self.after.push(label.as_system_label().as_label());
self self
} }
fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> ParallelSystemDescriptor { fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> ParallelSystemDescriptor {
self.ambiguity_sets.push(Box::new(set)); self.ambiguity_sets.push(set.as_label());
self self
} }
} }
@ -232,10 +232,10 @@ pub(crate) enum InsertionPoint {
pub struct ExclusiveSystemDescriptor { pub struct ExclusiveSystemDescriptor {
pub(crate) system: Box<dyn ExclusiveSystem>, pub(crate) system: Box<dyn ExclusiveSystem>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>, pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<BoxedSystemLabel>, pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<BoxedSystemLabel>, pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<BoxedSystemLabel>, pub(crate) after: Vec<SystemLabelId>,
pub(crate) ambiguity_sets: Vec<BoxedAmbiguitySetLabel>, pub(crate) ambiguity_sets: Vec<AmbiguitySetLabelId>,
pub(crate) insertion_point: InsertionPoint, pub(crate) insertion_point: InsertionPoint,
} }
@ -293,22 +293,22 @@ impl ExclusiveSystemDescriptorCoercion for ExclusiveSystemDescriptor {
} }
fn label(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor { fn label(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.labels.push(Box::new(label)); self.labels.push(label.as_label());
self self
} }
fn before(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor { fn before(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.before.push(Box::new(label)); self.before.push(label.as_label());
self self
} }
fn after(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor { fn after(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.after.push(Box::new(label)); self.after.push(label.as_label());
self self
} }
fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> ExclusiveSystemDescriptor { fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> ExclusiveSystemDescriptor {
self.ambiguity_sets.push(Box::new(set)); self.ambiguity_sets.push(set.as_label());
self self
} }

View file

@ -1,7 +1,6 @@
use crate::schedule::{ use crate::schedule::{
AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria, AmbiguitySetLabel, AmbiguitySetLabelId, IntoRunCriteria, IntoSystemDescriptor,
IntoSystemDescriptor, RunCriteriaDescriptorOrLabel, State, StateData, SystemDescriptor, RunCriteriaDescriptorOrLabel, State, StateData, SystemDescriptor, SystemLabel, SystemLabelId,
SystemLabel,
}; };
use crate::system::AsSystemLabel; use crate::system::AsSystemLabel;
@ -10,10 +9,10 @@ use crate::system::AsSystemLabel;
pub struct SystemSet { pub struct SystemSet {
pub(crate) systems: Vec<SystemDescriptor>, pub(crate) systems: Vec<SystemDescriptor>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>, pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<BoxedSystemLabel>, pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<BoxedSystemLabel>, pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<BoxedSystemLabel>, pub(crate) after: Vec<SystemLabelId>,
pub(crate) ambiguity_sets: Vec<BoxedAmbiguitySetLabel>, pub(crate) ambiguity_sets: Vec<AmbiguitySetLabelId>,
} }
impl SystemSet { impl SystemSet {
@ -72,7 +71,7 @@ impl SystemSet {
#[must_use] #[must_use]
pub fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> Self { pub fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> Self {
self.ambiguity_sets.push(Box::new(set)); self.ambiguity_sets.push(set.as_label());
self self
} }
@ -90,19 +89,19 @@ impl SystemSet {
#[must_use] #[must_use]
pub fn label(mut self, label: impl SystemLabel) -> Self { pub fn label(mut self, label: impl SystemLabel) -> Self {
self.labels.push(Box::new(label)); self.labels.push(label.as_label());
self self
} }
#[must_use] #[must_use]
pub fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> Self { pub fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> Self {
self.before.push(Box::new(label.as_system_label())); self.before.push(label.as_system_label().as_label());
self self
} }
#[must_use] #[must_use]
pub fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> Self { pub fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> Self {
self.after.push(Box::new(label.as_system_label())); self.after.push(label.as_system_label().as_label());
self self
} }

View file

@ -4,7 +4,7 @@ use crate::{
component::ComponentId, component::ComponentId,
prelude::FromWorld, prelude::FromWorld,
query::{Access, FilteredAccessSet}, query::{Access, FilteredAccessSet},
schedule::SystemLabel, schedule::{SystemLabel, SystemLabelId},
system::{ system::{
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemParam, SystemParamFetch, check_system_change_tick, ReadOnlySystemParamFetch, System, SystemParam, SystemParamFetch,
SystemParamItem, SystemParamState, SystemParamItem, SystemParamState,
@ -12,7 +12,7 @@ use crate::{
world::{World, WorldId}, world::{World, WorldId},
}; };
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
use std::{borrow::Cow, fmt::Debug, hash::Hash, marker::PhantomData}; use std::{borrow::Cow, fmt::Debug, marker::PhantomData};
/// The metadata of a [`System`]. /// The metadata of a [`System`].
#[derive(Clone)] #[derive(Clone)]
@ -444,14 +444,21 @@ where
self.system_meta.name.as_ref(), self.system_meta.name.as_ref(),
); );
} }
fn default_labels(&self) -> Vec<Box<dyn SystemLabel>> { fn default_labels(&self) -> Vec<SystemLabelId> {
vec![Box::new(self.func.as_system_label())] vec![self.func.as_system_label().as_label()]
} }
} }
/// A [`SystemLabel`] that was automatically generated for a system on the basis of its `TypeId`. /// A [`SystemLabel`] that was automatically generated for a system on the basis of its `TypeId`.
pub struct SystemTypeIdLabel<T: 'static>(PhantomData<fn() -> T>); pub struct SystemTypeIdLabel<T: 'static>(PhantomData<fn() -> T>);
impl<T: 'static> SystemLabel for SystemTypeIdLabel<T> {
#[inline]
fn as_str(&self) -> &'static str {
std::any::type_name::<T>()
}
}
impl<T> Debug for SystemTypeIdLabel<T> { impl<T> Debug for SystemTypeIdLabel<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SystemTypeIdLabel") f.debug_tuple("SystemTypeIdLabel")
@ -459,34 +466,14 @@ impl<T> Debug for SystemTypeIdLabel<T> {
.finish() .finish()
} }
} }
impl<T> Hash for SystemTypeIdLabel<T> {
fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {
// All SystemTypeIds of a given type are the same.
}
}
impl<T> Clone for SystemTypeIdLabel<T> { impl<T> Clone for SystemTypeIdLabel<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(PhantomData) *self
} }
} }
impl<T> Copy for SystemTypeIdLabel<T> {} impl<T> Copy for SystemTypeIdLabel<T> {}
impl<T> PartialEq for SystemTypeIdLabel<T> {
#[inline]
fn eq(&self, _other: &Self) -> bool {
// All labels of a given type are equal, as they will all have the same type id
true
}
}
impl<T> Eq for SystemTypeIdLabel<T> {}
impl<T> SystemLabel for SystemTypeIdLabel<T> {
fn dyn_clone(&self) -> Box<dyn SystemLabel> {
Box::new(*self)
}
}
/// A trait implemented for all functions that can be used as [`System`]s. /// A trait implemented for all functions that can be used as [`System`]s.
/// ///
/// This trait can be useful for making your own systems which accept other systems, /// This trait can be useful for making your own systems which accept other systems,
@ -612,24 +599,21 @@ all_tuples!(impl_system_function, 0, 16, F);
/// Used to implicitly convert systems to their default labels. For example, it will convert /// Used to implicitly convert systems to their default labels. For example, it will convert
/// "system functions" to their [`SystemTypeIdLabel`]. /// "system functions" to their [`SystemTypeIdLabel`].
pub trait AsSystemLabel<Marker> { pub trait AsSystemLabel<Marker> {
type SystemLabel: SystemLabel; fn as_system_label(&self) -> SystemLabelId;
fn as_system_label(&self) -> Self::SystemLabel;
} }
impl<In, Out, Param: SystemParam, Marker, T: SystemParamFunction<In, Out, Param, Marker>> impl<In, Out, Param: SystemParam, Marker, T: SystemParamFunction<In, Out, Param, Marker>>
AsSystemLabel<(In, Out, Param, Marker)> for T AsSystemLabel<(In, Out, Param, Marker)> for T
{ {
type SystemLabel = SystemTypeIdLabel<Self>; #[inline]
fn as_system_label(&self) -> SystemLabelId {
fn as_system_label(&self) -> Self::SystemLabel { SystemTypeIdLabel::<T>(PhantomData).as_label()
SystemTypeIdLabel(PhantomData::<fn() -> Self>)
} }
} }
impl<T: SystemLabel + Clone> AsSystemLabel<()> for T { impl<T: SystemLabel> AsSystemLabel<()> for T {
type SystemLabel = T; #[inline]
fn as_system_label(&self) -> SystemLabelId {
fn as_system_label(&self) -> Self::SystemLabel { self.as_label()
self.clone()
} }
} }

View file

@ -2,7 +2,7 @@ use bevy_utils::tracing::warn;
use crate::{ use crate::{
archetype::ArchetypeComponentId, change_detection::MAX_CHANGE_AGE, component::ComponentId, archetype::ArchetypeComponentId, change_detection::MAX_CHANGE_AGE, component::ComponentId,
query::Access, schedule::SystemLabel, world::World, query::Access, schedule::SystemLabelId, world::World,
}; };
use std::borrow::Cow; use std::borrow::Cow;
@ -56,7 +56,7 @@ pub trait System: Send + Sync + 'static {
fn update_archetype_component_access(&mut self, world: &World); fn update_archetype_component_access(&mut self, world: &World);
fn check_change_tick(&mut self, change_tick: u32); fn check_change_tick(&mut self, change_tick: u32);
/// The default labels for the system /// The default labels for the system
fn default_labels(&self) -> Vec<Box<dyn SystemLabel>> { fn default_labels(&self) -> Vec<SystemLabelId> {
Vec::new() Vec::new()
} }
} }

View file

@ -118,12 +118,41 @@ pub fn derive_label(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStr
where_token: Default::default(), where_token: Default::default(),
predicates: Default::default(), predicates: Default::default(),
}); });
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap()); where_clause
.predicates
.push(syn::parse2(quote! { Self: 'static }).unwrap());
let as_str = match input.data {
syn::Data::Struct(d) => match d.fields {
syn::Fields::Unit => {
let lit = ident.to_string();
quote! { #lit }
}
_ => panic!("Labels cannot contain data."),
},
syn::Data::Enum(d) => {
let arms = d.variants.iter().map(|v| match v.fields {
syn::Fields::Unit => {
let mut path = syn::Path::from(ident.clone());
path.segments.push(v.ident.clone().into());
let lit = format!("{ident}::{}", v.ident.clone());
quote! { #path => #lit }
}
_ => panic!("Label variants cannot contain data."),
});
quote! {
match self {
#(#arms),*
}
}
}
syn::Data::Union(_) => panic!("Unions cannot be used as labels."),
};
(quote! { (quote! {
impl #impl_generics #trait_path for #ident #ty_generics #where_clause { impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> std::boxed::Box<dyn #trait_path> { fn as_str(&self) -> &'static str {
std::boxed::Box::new(std::clone::Clone::clone(self)) #as_str
} }
} }
}) })

View file

@ -14,6 +14,7 @@ tracing = { version = "0.1", default-features = false, features = ["std"] }
instant = { version = "0.1", features = ["wasm-bindgen"] } instant = { version = "0.1", features = ["wasm-bindgen"] }
uuid = { version = "1.1", features = ["v4", "serde"] } uuid = { version = "1.1", features = ["v4", "serde"] }
hashbrown = { version = "0.12", features = ["serde"] } hashbrown = { version = "0.12", features = ["serde"] }
concat-idents = "1"
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = {version = "0.2.0", features = ["js"]} getrandom = {version = "0.2.0", features = ["js"]}

View file

@ -47,6 +47,9 @@ where
} }
} }
#[doc(hidden)]
pub use concat_idents::concat_idents;
/// Macro to define a new label trait /// Macro to define a new label trait
/// ///
/// # Example /// # Example
@ -57,45 +60,57 @@ where
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! define_label { macro_rules! define_label {
($label_trait_name:ident) => { ($label_name:ident) => {
/// Defines a set of strongly-typed labels for a class of objects $crate::label::concat_idents!(id_name = $label_name, Id {
pub trait $label_trait_name:
$crate::label::DynHash + ::std::fmt::Debug + Send + Sync + 'static
{
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn $label_trait_name>;
}
impl PartialEq for dyn $label_trait_name { /// Stores one of a set of strongly-typed labels for a class of objects.
fn eq(&self, other: &Self) -> bool { #[derive(Clone, Copy, PartialEq, Eq, Hash)]
self.dyn_eq(other.as_dyn_eq()) pub struct id_name(::core::any::TypeId, &'static str);
impl ::core::fmt::Debug for id_name {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "{}", self.1)
}
} }
}
impl Eq for dyn $label_trait_name {} /// Types that can be converted to a(n) [`id_name`].
///
impl ::std::hash::Hash for dyn $label_trait_name { /// Check the docs for [`define_label`](bevy_ecs::define_label) for more info.
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) { pub trait $label_name: 'static {
self.dyn_hash(state); /// Converts this type into an opaque, strongly-typed label.
fn as_label(&self) -> id_name {
let id = self.type_id();
let label = self.as_str();
id_name(id, label)
}
/// Returns the [`TypeId`] used to differentiate labels.
fn type_id(&self) -> ::core::any::TypeId {
::core::any::TypeId::of::<Self>()
}
/// Returns the representation of this label as a string literal.
///
/// In cases where you absolutely need a label to be determined at runtime,
/// you can use [`Box::leak`] to get a `'static` reference.
fn as_str(&self) -> &'static str;
} }
}
impl Clone for Box<dyn $label_trait_name> { impl $label_name for id_name {
fn clone(&self) -> Self { fn as_label(&self) -> Self {
self.dyn_clone() *self
}
fn type_id(&self) -> ::core::any::TypeId {
self.0
}
fn as_str(&self) -> &'static str {
self.1
}
} }
}
impl $label_trait_name for ::std::borrow::Cow<'static, str> { impl $label_name for &'static str {
fn dyn_clone(&self) -> Box<dyn $label_trait_name> { fn as_str(&self) -> Self {
Box::new(self.clone()) self
}
} }
} });
impl $label_trait_name for &'static str {
fn dyn_clone(&self) -> Box<dyn $label_trait_name> {
Box::new(<&str>::clone(self))
}
}
}; };
} }