Skip to main content

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}