Skip to main content

elevator_core/components/
destination_queue.rs

1//! Per-elevator destination queue (FIFO of target stop `EntityId`s).
2//!
3//! Games can push stops to the back or front of the queue, or clear it
4//! entirely, without writing a custom `DispatchStrategy`. This is the
5//! imperative-dispatch escape hatch for scripted scenarios.
6//!
7//! The built-in dispatch also writes to the queue (via
8//! [`Simulation::push_destination`](crate::sim::Simulation::push_destination)),
9//! so the queue is always in sync with the elevator's current target.
10
11use crate::entity::EntityId;
12use serde::{Deserialize, Serialize};
13
14/// FIFO queue of target stop `EntityId`s for a single elevator.
15///
16/// Adjacent duplicates are collapsed on push:
17///
18/// - `push_back` is a no-op if the *last* entry already equals the new stop.
19/// - `push_front` is a no-op if the *first* entry already equals the new stop.
20///
21/// Games interact with the queue via
22/// [`Simulation::push_destination`](crate::sim::Simulation::push_destination),
23/// [`Simulation::push_destination_front`](crate::sim::Simulation::push_destination_front),
24/// and [`Simulation::clear_destinations`](crate::sim::Simulation::clear_destinations).
25#[derive(Debug, Clone, Default, Serialize, Deserialize)]
26pub struct DestinationQueue {
27    /// Ordered FIFO of target stop `EntityId`s.
28    queue: Vec<EntityId>,
29}
30
31impl DestinationQueue {
32    /// Create an empty queue.
33    #[must_use]
34    pub const fn new() -> Self {
35        Self { queue: Vec::new() }
36    }
37
38    /// Read-only view of the current queue in FIFO order.
39    #[must_use]
40    pub fn queue(&self) -> &[EntityId] {
41        &self.queue
42    }
43
44    /// `true` if the queue contains no entries.
45    #[must_use]
46    pub const fn is_empty(&self) -> bool {
47        self.queue.is_empty()
48    }
49
50    /// Number of entries in the queue.
51    #[must_use]
52    pub const fn len(&self) -> usize {
53        self.queue.len()
54    }
55
56    /// The stop at the front of the queue (next destination).
57    #[must_use]
58    pub fn front(&self) -> Option<EntityId> {
59        self.queue.first().copied()
60    }
61
62    /// Push a stop onto the back of the queue.
63    ///
64    /// Returns `true` if the stop was actually appended. Returns `false` if
65    /// the queue is non-empty and its last entry already equals `stop`
66    /// (adjacent-duplicate dedup).
67    pub(crate) fn push_back(&mut self, stop: EntityId) -> bool {
68        if self.queue.last() == Some(&stop) {
69            return false;
70        }
71        self.queue.push(stop);
72        true
73    }
74
75    /// Insert a stop at the front of the queue (jump to this destination next).
76    ///
77    /// Returns `true` if the stop was actually inserted. Returns `false` if
78    /// the queue is non-empty and its first entry already equals `stop`.
79    pub(crate) fn push_front(&mut self, stop: EntityId) -> bool {
80        if self.queue.first() == Some(&stop) {
81            return false;
82        }
83        self.queue.insert(0, stop);
84        true
85    }
86
87    /// Drain all entries.
88    pub(crate) fn clear(&mut self) {
89        self.queue.clear();
90    }
91
92    /// Replace the queue contents with `stops` (order preserved).
93    ///
94    /// Used by direction-aware dispatch strategies that rebuild the
95    /// queue as a two-run monotone sequence.
96    pub(crate) fn replace(&mut self, stops: Vec<EntityId>) {
97        self.queue = stops;
98    }
99
100    /// Retain only entries that satisfy `predicate`.
101    ///
102    /// Used by `remove_stop` to scrub references to a despawned stop.
103    pub(crate) fn retain(&mut self, mut predicate: impl FnMut(EntityId) -> bool) {
104        self.queue.retain(|&eid| predicate(eid));
105    }
106
107    /// `true` if the queue contains `stop` anywhere.
108    #[must_use]
109    pub fn contains(&self, stop: &EntityId) -> bool {
110        self.queue.contains(stop)
111    }
112
113    /// Remove and return the front entry.
114    pub(crate) fn pop_front(&mut self) -> Option<EntityId> {
115        (!self.queue.is_empty()).then(|| self.queue.remove(0))
116    }
117}