elevator_core/events.rs
1//! Simulation event bus and typed event channels.
2
3use crate::entity::EntityId;
4use crate::error::{RejectionContext, RejectionReason};
5use crate::ids::GroupId;
6use ordered_float::OrderedFloat;
7use serde::{Deserialize, Serialize};
8
9/// Events emitted by the simulation during ticks.
10///
11/// All entity references use `EntityId`. Games can look up additional
12/// component data on the referenced entity if needed.
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[non_exhaustive]
15pub enum Event {
16 // -- Elevator events --
17 /// An elevator departed from a stop.
18 ElevatorDeparted {
19 /// The elevator that departed.
20 elevator: EntityId,
21 /// The stop it departed from.
22 from_stop: EntityId,
23 /// The tick when departure occurred.
24 tick: u64,
25 },
26 /// An elevator arrived at a stop.
27 ElevatorArrived {
28 /// The elevator that arrived.
29 elevator: EntityId,
30 /// The stop it arrived at.
31 at_stop: EntityId,
32 /// The tick when arrival occurred.
33 tick: u64,
34 },
35 /// An elevator's doors finished opening.
36 DoorOpened {
37 /// The elevator whose doors opened.
38 elevator: EntityId,
39 /// The tick when the doors opened.
40 tick: u64,
41 },
42 /// An elevator's doors finished closing.
43 DoorClosed {
44 /// The elevator whose doors closed.
45 elevator: EntityId,
46 /// The tick when the doors closed.
47 tick: u64,
48 },
49 /// Emitted when an elevator passes a stop without stopping.
50 /// Games/dispatch can use this to decide whether to add stops mid-travel.
51 PassingFloor {
52 /// The elevator passing by.
53 elevator: EntityId,
54 /// The stop being passed.
55 stop: EntityId,
56 /// Direction: true = moving up, false = moving down.
57 moving_up: bool,
58 /// The tick when the pass occurred.
59 tick: u64,
60 },
61
62 // -- Rider events (unified: passengers, cargo, any rideable entity) --
63 /// A new rider appeared at a stop and wants to travel.
64 RiderSpawned {
65 /// The spawned rider entity.
66 rider: EntityId,
67 /// The stop where the rider appeared.
68 origin: EntityId,
69 /// The stop the rider wants to reach.
70 destination: EntityId,
71 /// The tick when the rider spawned.
72 tick: u64,
73 },
74 /// A rider boarded an elevator.
75 RiderBoarded {
76 /// The rider that boarded.
77 rider: EntityId,
78 /// The elevator the rider boarded.
79 elevator: EntityId,
80 /// The tick when boarding occurred.
81 tick: u64,
82 },
83 /// A rider exited an elevator at a stop.
84 #[serde(alias = "RiderAlighted")]
85 RiderExited {
86 /// The rider that exited.
87 rider: EntityId,
88 /// The elevator the rider exited.
89 elevator: EntityId,
90 /// The stop where the rider exited.
91 stop: EntityId,
92 /// The tick when exiting occurred.
93 tick: u64,
94 },
95 /// A rider was rejected from boarding (e.g., over capacity).
96 RiderRejected {
97 /// The rider that was rejected.
98 rider: EntityId,
99 /// The elevator that rejected the rider.
100 elevator: EntityId,
101 /// The reason for rejection.
102 reason: RejectionReason,
103 /// Additional numeric context for the rejection.
104 context: Option<RejectionContext>,
105 /// The tick when rejection occurred.
106 tick: u64,
107 },
108 /// A rider gave up waiting and left the stop.
109 RiderAbandoned {
110 /// The rider that abandoned.
111 rider: EntityId,
112 /// The stop the rider left.
113 stop: EntityId,
114 /// The tick when abandonment occurred.
115 tick: u64,
116 },
117
118 /// A rider was ejected from an elevator (due to disable or despawn).
119 ///
120 /// The rider is moved to `Waiting` phase at the nearest stop.
121 RiderEjected {
122 /// The rider that was ejected.
123 rider: EntityId,
124 /// The elevator the rider was ejected from.
125 elevator: EntityId,
126 /// The stop the rider was placed at.
127 stop: EntityId,
128 /// The tick when ejection occurred.
129 tick: u64,
130 },
131
132 // -- Dispatch events --
133 /// An elevator was assigned to serve a stop by the dispatcher.
134 ElevatorAssigned {
135 /// The elevator that was assigned.
136 elevator: EntityId,
137 /// The stop it was assigned to serve.
138 stop: EntityId,
139 /// The tick when the assignment occurred.
140 tick: u64,
141 },
142
143 // -- Topology lifecycle events --
144 /// A new stop was added to the simulation.
145 StopAdded {
146 /// The new stop entity.
147 stop: EntityId,
148 /// The line the stop was added to.
149 line: EntityId,
150 /// The group the stop was added to.
151 group: GroupId,
152 /// The tick when the stop was added.
153 tick: u64,
154 },
155 /// A new elevator was added to the simulation.
156 ElevatorAdded {
157 /// The new elevator entity.
158 elevator: EntityId,
159 /// The line the elevator was added to.
160 line: EntityId,
161 /// The group the elevator was added to.
162 group: GroupId,
163 /// The tick when the elevator was added.
164 tick: u64,
165 },
166 /// An entity was disabled.
167 EntityDisabled {
168 /// The entity that was disabled.
169 entity: EntityId,
170 /// The tick when it was disabled.
171 tick: u64,
172 },
173 /// An entity was re-enabled.
174 EntityEnabled {
175 /// The entity that was re-enabled.
176 entity: EntityId,
177 /// The tick when it was enabled.
178 tick: u64,
179 },
180 /// A rider's route was invalidated due to topology change.
181 ///
182 /// Emitted when a stop on a rider's route is disabled or removed.
183 /// If no alternative is found, the rider will abandon after a grace period.
184 RouteInvalidated {
185 /// The affected rider.
186 rider: EntityId,
187 /// The stop that caused the invalidation.
188 affected_stop: EntityId,
189 /// Why the route was invalidated.
190 reason: RouteInvalidReason,
191 /// The tick when invalidation occurred.
192 tick: u64,
193 },
194 /// A rider was manually rerouted via `sim.reroute()` or `sim.reroute_rider()`.
195 RiderRerouted {
196 /// The rerouted rider.
197 rider: EntityId,
198 /// The new destination stop.
199 new_destination: EntityId,
200 /// The tick when rerouting occurred.
201 tick: u64,
202 },
203
204 /// A rider settled at a stop, becoming a resident.
205 RiderSettled {
206 /// The rider that settled.
207 rider: EntityId,
208 /// The stop where the rider settled.
209 stop: EntityId,
210 /// The tick when settlement occurred.
211 tick: u64,
212 },
213 /// A rider was removed from the simulation.
214 RiderDespawned {
215 /// The rider that was removed.
216 rider: EntityId,
217 /// The tick when despawn occurred.
218 tick: u64,
219 },
220
221 // -- Line lifecycle events --
222 /// A line was added to the simulation.
223 LineAdded {
224 /// The new line entity.
225 line: EntityId,
226 /// The group the line was added to.
227 group: GroupId,
228 /// The tick when the line was added.
229 tick: u64,
230 },
231 /// A line was removed from the simulation.
232 LineRemoved {
233 /// The removed line entity.
234 line: EntityId,
235 /// The group the line belonged to.
236 group: GroupId,
237 /// The tick when the line was removed.
238 tick: u64,
239 },
240 /// A line was reassigned to a different group.
241 LineReassigned {
242 /// The line entity that was reassigned.
243 line: EntityId,
244 /// The group the line was previously in.
245 old_group: GroupId,
246 /// The group the line was moved to.
247 new_group: GroupId,
248 /// The tick when reassignment occurred.
249 tick: u64,
250 },
251 /// An elevator was reassigned to a different line.
252 ElevatorReassigned {
253 /// The elevator that was reassigned.
254 elevator: EntityId,
255 /// The line the elevator was previously on.
256 old_line: EntityId,
257 /// The line the elevator was moved to.
258 new_line: EntityId,
259 /// The tick when reassignment occurred.
260 tick: u64,
261 },
262
263 // -- Repositioning events --
264 /// An elevator is being repositioned to improve coverage.
265 ///
266 /// Emitted when an idle elevator begins moving to a new position
267 /// as decided by the [`RepositionStrategy`](crate::dispatch::RepositionStrategy).
268 ElevatorRepositioning {
269 /// The elevator being repositioned.
270 elevator: EntityId,
271 /// The stop it is being sent to.
272 to_stop: EntityId,
273 /// The tick when repositioning began.
274 tick: u64,
275 },
276 /// An elevator completed repositioning and arrived at its target.
277 ///
278 /// Note: this is detected by the movement system — the elevator
279 /// arrives just like any other movement. Games can distinguish
280 /// repositioning arrivals from dispatch arrivals by tracking
281 /// which elevators received `ElevatorRepositioning` events.
282 ElevatorRepositioned {
283 /// The elevator that completed repositioning.
284 elevator: EntityId,
285 /// The stop it arrived at.
286 at_stop: EntityId,
287 /// The tick when it arrived.
288 tick: u64,
289 },
290
291 /// An elevator's service mode was changed.
292 ServiceModeChanged {
293 /// The elevator whose mode changed.
294 elevator: EntityId,
295 /// The previous service mode.
296 from: crate::components::ServiceMode,
297 /// The new service mode.
298 to: crate::components::ServiceMode,
299 /// The tick when the change occurred.
300 tick: u64,
301 },
302
303 // -- Observability events --
304 /// Energy consumed/regenerated by an elevator this tick.
305 ///
306 /// Requires the `energy` feature.
307 #[cfg(feature = "energy")]
308 EnergyConsumed {
309 /// The elevator that consumed energy.
310 elevator: EntityId,
311 /// Energy consumed this tick.
312 consumed: OrderedFloat<f64>,
313 /// Energy regenerated this tick.
314 regenerated: OrderedFloat<f64>,
315 /// The tick when energy was recorded.
316 tick: u64,
317 },
318
319 /// An elevator's load changed (rider boarded or exited).
320 ///
321 /// Emitted immediately after [`RiderBoarded`](Self::RiderBoarded) or
322 /// [`RiderExited`](Self::RiderExited). Useful for real-time capacity
323 /// bar displays in game UIs.
324 ///
325 /// Load values use [`OrderedFloat`] for
326 /// `Eq` compatibility. Dereference to get the inner `f64`:
327 ///
328 /// ```rust,ignore
329 /// use elevator_core::events::Event;
330 ///
331 /// if let Event::CapacityChanged { current_load, capacity, .. } = event {
332 /// let pct = *current_load / *capacity * 100.0;
333 /// println!("Elevator at {pct:.0}% capacity");
334 /// }
335 /// ```
336 CapacityChanged {
337 /// The elevator whose load changed.
338 elevator: EntityId,
339 /// Current total weight aboard after the change.
340 current_load: OrderedFloat<f64>,
341 /// Maximum weight capacity of the elevator.
342 capacity: OrderedFloat<f64>,
343 /// The tick when the change occurred.
344 tick: u64,
345 },
346
347 /// An elevator became idle (no more assignments or repositioning).
348 ElevatorIdle {
349 /// The elevator that became idle.
350 elevator: EntityId,
351 /// The stop where it became idle (if at a stop).
352 at_stop: Option<EntityId>,
353 /// The tick when it became idle.
354 tick: u64,
355 },
356
357 /// An elevator's direction indicator lamps changed.
358 ///
359 /// Emitted by the dispatch phase when the pair
360 /// `(going_up, going_down)` transitions to a new value — e.g. when
361 /// a car is assigned a target above it (up-only), below it (down-only),
362 /// or returns to idle (both lamps lit).
363 ///
364 /// Games can use this for UI (lighting up-arrow / down-arrow indicators
365 /// on a car) without polling the elevator each tick.
366 DirectionIndicatorChanged {
367 /// The elevator whose indicator lamps changed.
368 elevator: EntityId,
369 /// New state of the up-direction lamp.
370 going_up: bool,
371 /// New state of the down-direction lamp.
372 going_down: bool,
373 /// The tick when the change occurred.
374 tick: u64,
375 },
376
377 /// An elevator was permanently removed from the simulation.
378 ///
379 /// Distinct from [`Event::EntityDisabled`] — a disabled elevator can be
380 /// re-enabled, but a removed elevator is despawned.
381 ElevatorRemoved {
382 /// The elevator that was removed.
383 elevator: EntityId,
384 /// The line it belonged to.
385 line: EntityId,
386 /// The group it belonged to.
387 group: GroupId,
388 /// The tick when removal occurred.
389 tick: u64,
390 },
391
392 /// A stop was queued as a destination for an elevator.
393 ///
394 /// Emitted by [`Simulation::push_destination`](crate::sim::Simulation::push_destination),
395 /// [`Simulation::push_destination_front`](crate::sim::Simulation::push_destination_front),
396 /// and the built-in dispatch phase whenever it actually appends a stop
397 /// to an elevator's [`DestinationQueue`](crate::components::DestinationQueue).
398 /// Adjacent-duplicate pushes (that are deduplicated) do not emit.
399 DestinationQueued {
400 /// The elevator whose queue was updated.
401 elevator: EntityId,
402 /// The stop that was queued.
403 stop: EntityId,
404 /// The tick when the push occurred.
405 tick: u64,
406 },
407
408 /// A stop was permanently removed from the simulation.
409 ///
410 /// Distinct from [`Event::EntityDisabled`] — a disabled stop can be
411 /// re-enabled, but a removed stop is despawned.
412 StopRemoved {
413 /// The stop that was removed.
414 stop: EntityId,
415 /// The tick when removal occurred.
416 tick: u64,
417 },
418
419 /// A manual door-control command was received and either applied
420 /// immediately or stored for later.
421 ///
422 /// Emitted by
423 /// [`Simulation::request_door_open`](crate::sim::Simulation::request_door_open),
424 /// [`Simulation::request_door_close`](crate::sim::Simulation::request_door_close),
425 /// [`Simulation::hold_door_open`](crate::sim::Simulation::hold_door_open),
426 /// and [`Simulation::cancel_door_hold`](crate::sim::Simulation::cancel_door_hold)
427 /// when the command is accepted. Paired with
428 /// [`Event::DoorCommandApplied`] when the command eventually takes effect.
429 DoorCommandQueued {
430 /// The elevator targeted by the command.
431 elevator: EntityId,
432 /// The command that was queued.
433 command: crate::door::DoorCommand,
434 /// The tick when the command was submitted.
435 tick: u64,
436 },
437 /// A queued door-control command actually took effect — doors began
438 /// opening/closing or a hold was applied.
439 DoorCommandApplied {
440 /// The elevator the command applied to.
441 elevator: EntityId,
442 /// The command that was applied.
443 command: crate::door::DoorCommand,
444 /// The tick when the command was applied.
445 tick: u64,
446 },
447
448 /// An elevator parameter was mutated at runtime via one of the
449 /// `Simulation::set_*` upgrade setters (e.g. buying a speed upgrade
450 /// in an RPG, or a scripted event changing capacity mid-game).
451 ///
452 /// Emitted immediately when the setter succeeds. Games can use this
453 /// to trigger score popups, SFX, or UI updates.
454 ElevatorUpgraded {
455 /// The elevator whose parameter changed.
456 elevator: EntityId,
457 /// Which field was changed.
458 field: UpgradeField,
459 /// Previous value of the field.
460 old: UpgradeValue,
461 /// New value of the field.
462 new: UpgradeValue,
463 /// The tick when the upgrade was applied.
464 tick: u64,
465 },
466
467 /// A velocity command was submitted to an elevator running in
468 /// [`ServiceMode::Manual`](crate::components::ServiceMode::Manual).
469 ///
470 /// Emitted by
471 /// [`Simulation::set_target_velocity`](crate::sim::Simulation::set_target_velocity)
472 /// and [`Simulation::emergency_stop`](crate::sim::Simulation::emergency_stop).
473 ManualVelocityCommanded {
474 /// The elevator targeted by the command.
475 elevator: EntityId,
476 /// The new target velocity (clamped to `[-max_speed, max_speed]`),
477 /// or `None` when the command clears the target.
478 target_velocity: Option<ordered_float::OrderedFloat<f64>>,
479 /// The tick when the command was submitted.
480 tick: u64,
481 },
482}
483
484/// Identifies which elevator parameter was changed in an
485/// [`Event::ElevatorUpgraded`].
486#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
487#[non_exhaustive]
488pub enum UpgradeField {
489 /// Maximum travel speed (distance/tick).
490 MaxSpeed,
491 /// Acceleration rate (distance/tick^2).
492 Acceleration,
493 /// Deceleration rate (distance/tick^2).
494 Deceleration,
495 /// Maximum weight the car can carry.
496 WeightCapacity,
497 /// Ticks for a door open/close transition.
498 DoorTransitionTicks,
499 /// Ticks the door stays fully open.
500 DoorOpenTicks,
501}
502
503impl std::fmt::Display for UpgradeField {
504 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
505 match self {
506 Self::MaxSpeed => write!(f, "max_speed"),
507 Self::Acceleration => write!(f, "acceleration"),
508 Self::Deceleration => write!(f, "deceleration"),
509 Self::WeightCapacity => write!(f, "weight_capacity"),
510 Self::DoorTransitionTicks => write!(f, "door_transition_ticks"),
511 Self::DoorOpenTicks => write!(f, "door_open_ticks"),
512 }
513 }
514}
515
516/// Old-or-new value carried by [`Event::ElevatorUpgraded`].
517///
518/// Uses [`OrderedFloat`] for the float variant so the event enum
519/// remains `Eq`-comparable alongside the other observability events.
520#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
521#[non_exhaustive]
522pub enum UpgradeValue {
523 /// A floating-point parameter value (speed, accel, decel, capacity).
524 Float(OrderedFloat<f64>),
525 /// An integral tick-count parameter value (door timings).
526 Ticks(u32),
527}
528
529impl UpgradeValue {
530 /// Construct a float-valued upgrade payload.
531 #[must_use]
532 pub const fn float(v: f64) -> Self {
533 Self::Float(OrderedFloat(v))
534 }
535
536 /// Construct a tick-valued upgrade payload.
537 #[must_use]
538 pub const fn ticks(v: u32) -> Self {
539 Self::Ticks(v)
540 }
541}
542
543impl std::fmt::Display for UpgradeValue {
544 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
545 match self {
546 Self::Float(v) => write!(f, "{}", **v),
547 Self::Ticks(v) => write!(f, "{v}"),
548 }
549 }
550}
551
552/// Reason a rider's route was invalidated.
553#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
554#[non_exhaustive]
555pub enum RouteInvalidReason {
556 /// A stop on the route was disabled.
557 StopDisabled,
558 /// No alternative stop is available in the same group.
559 NoAlternative,
560}
561
562/// Coarse-grained classification of an [`Event`].
563///
564/// Exposes the same grouping that the `Event` variants are already
565/// commented under, so consumers can filter a drained event stream with
566/// one match arm per category rather than enumerating ~25 variants.
567#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
568#[non_exhaustive]
569pub enum EventCategory {
570 /// Elevator motion, arrival/departure, and door state.
571 Elevator,
572 /// Rider lifecycle: spawn, board, exit, reject, abandon, despawn, settle, reroute.
573 Rider,
574 /// Dispatch decisions and bookkeeping.
575 Dispatch,
576 /// Runtime topology mutations (entities/lines/groups added, removed, reassigned).
577 Topology,
578 /// Idle-elevator repositioning activity.
579 Reposition,
580 /// Direction indicator lamp state changes.
581 Direction,
582 /// Observability and misc signals (capacity, service mode, energy, etc.).
583 Observability,
584}
585
586impl Event {
587 /// Classify this event into a coarse-grained [`EventCategory`].
588 ///
589 /// Useful when a consumer only cares about, say, rider activity and
590 /// wants to skip elevator-motion and topology chatter without
591 /// enumerating every variant. The exhaustive match inside guarantees
592 /// the method stays in sync when new variants are added.
593 #[must_use]
594 pub const fn category(&self) -> EventCategory {
595 match self {
596 Self::ElevatorDeparted { .. }
597 | Self::ElevatorArrived { .. }
598 | Self::DoorOpened { .. }
599 | Self::DoorClosed { .. }
600 | Self::DoorCommandQueued { .. }
601 | Self::DoorCommandApplied { .. }
602 | Self::PassingFloor { .. }
603 | Self::ElevatorIdle { .. } => EventCategory::Elevator,
604 Self::RiderSpawned { .. }
605 | Self::RiderBoarded { .. }
606 | Self::RiderExited { .. }
607 | Self::RiderRejected { .. }
608 | Self::RiderAbandoned { .. }
609 | Self::RiderEjected { .. }
610 | Self::RouteInvalidated { .. }
611 | Self::RiderRerouted { .. }
612 | Self::RiderSettled { .. }
613 | Self::RiderDespawned { .. } => EventCategory::Rider,
614 Self::ElevatorAssigned { .. } | Self::DestinationQueued { .. } => {
615 EventCategory::Dispatch
616 }
617 Self::StopAdded { .. }
618 | Self::StopRemoved { .. }
619 | Self::ElevatorAdded { .. }
620 | Self::ElevatorRemoved { .. }
621 | Self::EntityDisabled { .. }
622 | Self::EntityEnabled { .. }
623 | Self::LineAdded { .. }
624 | Self::LineRemoved { .. }
625 | Self::LineReassigned { .. }
626 | Self::ElevatorReassigned { .. } => EventCategory::Topology,
627 Self::ElevatorRepositioning { .. } | Self::ElevatorRepositioned { .. } => {
628 EventCategory::Reposition
629 }
630 Self::DirectionIndicatorChanged { .. } => EventCategory::Direction,
631 Self::ServiceModeChanged { .. }
632 | Self::CapacityChanged { .. }
633 | Self::ElevatorUpgraded { .. }
634 | Self::ManualVelocityCommanded { .. } => EventCategory::Observability,
635 #[cfg(feature = "energy")]
636 Self::EnergyConsumed { .. } => EventCategory::Observability,
637 }
638 }
639}
640
641/// Collects simulation events for consumers to drain.
642#[derive(Debug, Default)]
643pub struct EventBus {
644 /// The pending events not yet consumed.
645 events: Vec<Event>,
646}
647
648impl EventBus {
649 /// Pushes a new event onto the bus.
650 pub fn emit(&mut self, event: Event) {
651 self.events.push(event);
652 }
653
654 /// Returns and clears all pending events.
655 pub fn drain(&mut self) -> Vec<Event> {
656 std::mem::take(&mut self.events)
657 }
658
659 /// Returns a slice of all pending events without clearing them.
660 #[must_use]
661 pub fn peek(&self) -> &[Event] {
662 &self.events
663 }
664}
665
666/// A typed event channel for game-specific events.
667///
668/// Games insert this as a global resource on `World`:
669///
670/// ```
671/// use elevator_core::world::World;
672/// use elevator_core::events::EventChannel;
673///
674/// #[derive(Debug)]
675/// enum MyGameEvent { Foo, Bar }
676///
677/// let mut world = World::new();
678/// world.insert_resource(EventChannel::<MyGameEvent>::new());
679/// // Later:
680/// world.resource_mut::<EventChannel<MyGameEvent>>().unwrap().emit(MyGameEvent::Foo);
681/// ```
682#[derive(Debug)]
683pub struct EventChannel<T> {
684 /// Pending events not yet consumed.
685 events: Vec<T>,
686}
687
688impl<T> EventChannel<T> {
689 /// Create an empty event channel.
690 #[must_use]
691 pub const fn new() -> Self {
692 Self { events: Vec::new() }
693 }
694
695 /// Emit an event into the channel.
696 pub fn emit(&mut self, event: T) {
697 self.events.push(event);
698 }
699
700 /// Drain and return all pending events.
701 pub fn drain(&mut self) -> Vec<T> {
702 std::mem::take(&mut self.events)
703 }
704
705 /// Peek at pending events without clearing.
706 #[must_use]
707 pub fn peek(&self) -> &[T] {
708 &self.events
709 }
710
711 /// Check if the channel has no pending events.
712 #[must_use]
713 pub const fn is_empty(&self) -> bool {
714 self.events.is_empty()
715 }
716
717 /// Number of pending events.
718 #[must_use]
719 pub const fn len(&self) -> usize {
720 self.events.len()
721 }
722}
723
724impl<T> Default for EventChannel<T> {
725 fn default() -> Self {
726 Self::new()
727 }
728}