Skip to main content

elevator_core/components/
access.rs

1//! Access control component for restricting rider stop access.
2
3use crate::entity::EntityId;
4use serde::{Deserialize, Serialize};
5use std::collections::HashSet;
6
7/// Per-rider access control: which stops the rider is allowed to visit.
8///
9/// When absent from a rider entity, the rider has unrestricted access.
10/// When present, [`can_access`](Self::can_access) returns `true` either
11/// when the queried stop is in [`allowed_stops`](Self::allowed_stops)
12/// or when the set is empty — an empty set means "no restriction"
13/// (matches `AccessControl::default()` and the principle-of-least-
14/// surprise that an empty allowlist shouldn't silently block all access).
15/// To explicitly block all stops, use a sentinel set or a dedicated
16/// no-access wrapper. (#289)
17#[derive(Debug, Clone, Default, Serialize, Deserialize)]
18pub struct AccessControl {
19    /// Set of stop `EntityId`s this rider may visit.
20    ///
21    /// **Semantics**: empty set ⇒ unrestricted (allow all). Non-empty
22    /// set ⇒ allowlist; `can_access(stop)` is true iff `stop` is in
23    /// the set.
24    allowed_stops: HashSet<EntityId>,
25}
26
27impl AccessControl {
28    /// Create a new access control with the given set of allowed stops.
29    /// Pass an empty set for "no restriction" (unrestricted access).
30    #[must_use]
31    pub const fn new(allowed_stops: HashSet<EntityId>) -> Self {
32        Self { allowed_stops }
33    }
34
35    /// Check if the rider can access the given stop.
36    ///
37    /// An empty `allowed_stops` set is treated as "no restriction" —
38    /// `can_access` returns `true` for any stop. A non-empty set is an
39    /// allowlist; `stop` must be a member.
40    #[must_use]
41    pub fn can_access(&self, stop: EntityId) -> bool {
42        self.allowed_stops.is_empty() || self.allowed_stops.contains(&stop)
43    }
44
45    /// The set of allowed stop entity IDs. Empty means unrestricted.
46    #[must_use]
47    pub const fn allowed_stops(&self) -> &HashSet<EntityId> {
48        &self.allowed_stops
49    }
50}