Skip to main content

elevator_core/
stop.rs

1//! Stop identifiers and configuration.
2
3use crate::entity::EntityId;
4use serde::{Deserialize, Serialize};
5
6/// Numeric identifier for a stop along the shaft.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct StopId(pub u32);
9
10/// A reference to a stop by either its config-time [`StopId`] or its
11/// runtime [`EntityId`].
12///
13/// Methods on [`Simulation`](crate::sim::Simulation) that take a stop
14/// accept `impl Into<StopRef>`, so callers can pass either type directly.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum StopRef {
17    /// Config-time identifier.
18    ById(StopId),
19    /// Runtime entity identifier.
20    ByEntity(EntityId),
21}
22
23impl From<StopId> for StopRef {
24    fn from(id: StopId) -> Self {
25        Self::ById(id)
26    }
27}
28
29impl From<EntityId> for StopRef {
30    fn from(id: EntityId) -> Self {
31        Self::ByEntity(id)
32    }
33}
34
35impl std::fmt::Display for StopId {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(f, "StopId({})", self.0)
38    }
39}
40
41/// A stop at an arbitrary position along the elevator shaft.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct StopConfig {
44    /// The unique identifier for this stop.
45    pub id: StopId,
46    /// Human-readable name for this stop.
47    pub name: String,
48    /// Absolute position along the shaft axis (distance units from origin).
49    pub position: f64,
50}
51
52impl StopConfig {
53    /// Build a `Vec<StopConfig>` from a compact `(name, position)` slice.
54    ///
55    /// `StopId`s are assigned sequentially starting at 0. Useful for demos,
56    /// tests, and any sim whose stops don't need hand-picked identifiers.
57    ///
58    /// # Example
59    ///
60    /// ```
61    /// use elevator_core::stop::StopConfig;
62    ///
63    /// let stops = StopConfig::linear(&[
64    ///     ("Ground", 0.0),
65    ///     ("Floor 2", 4.0),
66    ///     ("Floor 3", 8.0),
67    /// ]);
68    /// assert_eq!(stops.len(), 3);
69    /// assert_eq!(stops[0].name, "Ground");
70    /// assert_eq!(stops[2].position, 8.0);
71    /// ```
72    #[must_use]
73    pub fn linear(stops: &[(&str, f64)]) -> Vec<Self> {
74        stops
75            .iter()
76            .enumerate()
77            .map(|(i, (name, position))| Self {
78                id: StopId(u32::try_from(i).unwrap_or(u32::MAX)),
79                name: (*name).to_owned(),
80                position: *position,
81            })
82            .collect()
83    }
84}