1use crate::ids::GroupId;
22use crate::world::World;
23use std::collections::HashMap;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[non_exhaustive]
31pub enum Phase {
32 AdvanceTransient,
34 Dispatch,
36 Movement,
38 Doors,
40 Loading,
42 Reposition,
44 AdvanceQueue,
46 Metrics,
48}
49
50impl std::fmt::Display for Phase {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 let s = match self {
58 Self::AdvanceTransient => "advance_transient",
59 Self::Dispatch => "dispatch",
60 Self::Movement => "movement",
61 Self::Doors => "doors",
62 Self::Loading => "loading",
63 Self::Reposition => "reposition",
64 Self::AdvanceQueue => "advance_queue",
65 Self::Metrics => "metrics",
66 };
67 f.write_str(s)
68 }
69}
70
71pub(crate) type PhaseHook = Box<dyn Fn(&mut World) + Send + Sync>;
73
74#[derive(Default)]
76pub(crate) struct PhaseHooks {
77 before: HashMap<Phase, Vec<PhaseHook>>,
79 after: HashMap<Phase, Vec<PhaseHook>>,
81 before_group: HashMap<(Phase, GroupId), Vec<PhaseHook>>,
83 after_group: HashMap<(Phase, GroupId), Vec<PhaseHook>>,
85}
86
87impl PhaseHooks {
88 pub(crate) fn run_before(&self, phase: Phase, world: &mut World) {
90 if let Some(hooks) = self.before.get(&phase) {
91 for hook in hooks {
92 hook(world);
93 }
94 Self::debug_check_invariants(phase, world);
95 }
96 }
97
98 pub(crate) fn run_after(&self, phase: Phase, world: &mut World) {
100 if let Some(hooks) = self.after.get(&phase) {
101 for hook in hooks {
102 hook(world);
103 }
104 Self::debug_check_invariants(phase, world);
105 }
106 }
107
108 #[cfg(debug_assertions)]
110 fn debug_check_invariants(phase: Phase, world: &World) {
111 for (eid, _, elev) in world.iter_elevators() {
112 for &rider_id in &elev.riders {
113 debug_assert!(
114 world.is_alive(rider_id),
115 "hook after {phase:?}: elevator {eid:?} references dead rider {rider_id:?}"
116 );
117 }
118 }
119 }
120
121 #[cfg(not(debug_assertions))]
122 fn debug_check_invariants(_phase: Phase, _world: &World) {}
123
124 pub(crate) fn add_before(&mut self, phase: Phase, hook: PhaseHook) {
126 self.before.entry(phase).or_default().push(hook);
127 }
128
129 pub(crate) fn add_after(&mut self, phase: Phase, hook: PhaseHook) {
131 self.after.entry(phase).or_default().push(hook);
132 }
133
134 pub(crate) fn run_before_group(&self, phase: Phase, group: GroupId, world: &mut World) {
136 if let Some(hooks) = self.before_group.get(&(phase, group)) {
137 for hook in hooks {
138 hook(world);
139 }
140 }
141 }
142
143 pub(crate) fn run_after_group(&self, phase: Phase, group: GroupId, world: &mut World) {
145 if let Some(hooks) = self.after_group.get(&(phase, group)) {
146 for hook in hooks {
147 hook(world);
148 }
149 }
150 }
151
152 pub(crate) fn add_before_group(&mut self, phase: Phase, group: GroupId, hook: PhaseHook) {
154 self.before_group
155 .entry((phase, group))
156 .or_default()
157 .push(hook);
158 }
159
160 pub(crate) fn add_after_group(&mut self, phase: Phase, group: GroupId, hook: PhaseHook) {
162 self.after_group
163 .entry((phase, group))
164 .or_default()
165 .push(hook);
166 }
167}