use std::collections::HashMap;
use std::hash::Hash;
use super::SeatState;
#[derive(Clone, Debug, PartialEq, Eq)]
enum Slot<Key> {
Open,
Human(Key),
Held(Key),
}
#[derive(Clone, Debug, Default)]
pub struct SeatSlots<Key: Eq + Hash + Clone> {
slots: Vec<Slot<Key>>,
index: HashMap<Key, usize>,
}
impl<Key: Eq + Hash + Clone> SeatSlots<Key> {
pub fn new(capacity: usize) -> Self {
Self {
slots: vec![Slot::Open; capacity],
index: HashMap::new(),
}
}
pub fn first_open(&self) -> Option<usize> {
self.slots.iter().position(|slot| *slot == Slot::Open)
}
pub fn seat(&mut self, key: Key, seat: usize) {
debug_assert!(self.slots[seat] == Slot::Open, "seating into a non-open seat");
self.slots[seat] = Slot::Human(key.clone());
self.index.insert(key, seat);
}
pub fn hold(&mut self, key: &Key) -> Option<usize> {
let seat = *self.index.get(key)?;
if !matches!(self.slots[seat], Slot::Human(_)) {
return None;
}
self.slots[seat] = Slot::Held(key.clone());
Some(seat)
}
pub fn resume(&mut self, key: &Key) -> Option<usize> {
let seat = *self.index.get(key)?;
if !matches!(self.slots[seat], Slot::Held(_)) {
return None;
}
self.slots[seat] = Slot::Human(key.clone());
Some(seat)
}
pub fn open(&mut self, key: &Key) -> Option<usize> {
let seat = self.index.remove(key)?;
self.slots[seat] = Slot::Open;
Some(seat)
}
pub fn seat_of(&self, key: &Key) -> Option<usize> {
self.index.get(key).copied()
}
pub fn is_held(&self, key: &Key) -> bool {
self
.index
.get(key)
.is_some_and(|seat| matches!(self.slots[*seat], Slot::Held(_)))
}
pub fn state(&self, seat: usize) -> SeatState<'_, Key> {
match &self.slots[seat] {
Slot::Open => SeatState::Open,
Slot::Human(key) => SeatState::Human(key),
Slot::Held(key) => SeatState::Held(key),
}
}
pub fn capacity(&self) -> usize {
self.slots.len()
}
pub fn occupied_count(&self) -> usize {
self.index.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_lifecycle_open_human_held_open() {
let mut slots: SeatSlots<u64> = SeatSlots::new(2);
assert_eq!(slots.first_open(), Some(0));
slots.seat(1, 0);
assert_eq!(slots.state(0), SeatState::Human(&1));
assert_eq!(slots.first_open(), Some(1));
assert_eq!(slots.hold(&1), Some(0));
assert_eq!(slots.state(0), SeatState::Held(&1));
assert_eq!(slots.hold(&1), None, "a held seat holds once");
assert!(slots.is_held(&1));
assert_eq!(slots.first_open(), Some(1), "a held seat is not open");
assert_eq!(slots.resume(&1), Some(0));
assert_eq!(slots.state(0), SeatState::Human(&1));
assert_eq!(slots.resume(&1), None, "nothing to resume");
assert_eq!(slots.open(&1), Some(0));
assert_eq!(slots.state(0), SeatState::Open);
assert_eq!(slots.open(&1), None);
assert_eq!(slots.occupied_count(), 0);
}
#[test]
fn the_index_survives_the_hold() {
let mut slots: SeatSlots<u64> = SeatSlots::new(1);
slots.seat(9, 0);
slots.hold(&9);
assert_eq!(slots.seat_of(&9), Some(0), "a held seat is still theirs");
}
}