use crate::ids::GroupId;
use crate::world::World;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Phase {
AdvanceTransient,
Dispatch,
Movement,
Doors,
Loading,
Reposition,
AdvanceQueue,
Metrics,
}
impl std::fmt::Display for Phase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::AdvanceTransient => "advance_transient",
Self::Dispatch => "dispatch",
Self::Movement => "movement",
Self::Doors => "doors",
Self::Loading => "loading",
Self::Reposition => "reposition",
Self::AdvanceQueue => "advance_queue",
Self::Metrics => "metrics",
};
f.write_str(s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum When {
Before,
After,
}
pub(crate) type PhaseHook = Box<dyn Fn(&mut World) + Send + Sync>;
#[derive(Default)]
pub(crate) struct PhaseHooks {
hooks: HashMap<(Phase, When, Option<GroupId>), Vec<PhaseHook>>,
}
impl PhaseHooks {
fn add(&mut self, phase: Phase, when: When, group: Option<GroupId>, hook: PhaseHook) {
self.hooks
.entry((phase, when, group))
.or_default()
.push(hook);
}
fn run(&self, phase: Phase, when: When, group: Option<GroupId>, world: &mut World) -> bool {
let Some(hooks) = self.hooks.get(&(phase, when, group)) else {
return false;
};
for hook in hooks {
hook(world);
}
true
}
pub(crate) fn run_before(&self, phase: Phase, world: &mut World) {
if self.run(phase, When::Before, None, world) {
Self::debug_check_invariants(phase, world);
}
}
pub(crate) fn run_after(&self, phase: Phase, world: &mut World) {
if self.run(phase, When::After, None, world) {
Self::debug_check_invariants(phase, world);
}
}
#[cfg(debug_assertions)]
fn debug_check_invariants(phase: Phase, world: &World) {
for (eid, _, elev) in world.iter_elevators() {
for &rider_id in &elev.riders {
debug_assert!(
world.is_alive(rider_id),
"hook after {phase:?}: elevator {eid:?} references dead rider {rider_id:?}"
);
}
}
}
#[cfg(not(debug_assertions))]
fn debug_check_invariants(_phase: Phase, _world: &World) {}
pub(crate) fn add_before(&mut self, phase: Phase, hook: PhaseHook) {
self.add(phase, When::Before, None, hook);
}
pub(crate) fn add_after(&mut self, phase: Phase, hook: PhaseHook) {
self.add(phase, When::After, None, hook);
}
pub(crate) fn run_before_group(&self, phase: Phase, group: GroupId, world: &mut World) {
self.run(phase, When::Before, Some(group), world);
}
pub(crate) fn run_after_group(&self, phase: Phase, group: GroupId, world: &mut World) {
self.run(phase, When::After, Some(group), world);
}
pub(crate) fn add_before_group(&mut self, phase: Phase, group: GroupId, hook: PhaseHook) {
self.add(phase, When::Before, Some(group), hook);
}
pub(crate) fn add_after_group(&mut self, phase: Phase, group: GroupId, hook: PhaseHook) {
self.add(phase, When::After, Some(group), hook);
}
}