use std::collections::BTreeMap;
use std::hash::Hash;
use super::{Admission, Roster};
#[derive(Clone, Debug, Default)]
pub struct Crew<Key: Eq + Hash + Clone> {
seats: BTreeMap<usize, Key>,
}
impl<Key: Eq + Hash + Clone> Crew<Key> {
pub fn new() -> Self {
Self { seats: BTreeMap::new() }
}
pub fn fill(
&mut self,
roster: &mut Roster<Key>,
count: usize,
rank: u32,
key_of: impl Fn(usize) -> Key,
) -> Vec<usize> {
let mut taken = Vec::new();
for index in 0..count {
let key = key_of(index);
let Admission::Seated { seat, .. } = roster.admit_ranked(key.clone(), rank) else {
break;
};
self.seats.insert(seat, key);
taken.push(seat);
}
taken
}
pub fn holds(&self, seat: usize) -> bool {
self.seats.contains_key(&seat)
}
pub fn seats(&self) -> impl Iterator<Item = usize> + '_ {
self.seats.keys().copied()
}
pub fn len(&self) -> usize {
self.seats.len()
}
pub fn is_empty(&self) -> bool {
self.seats.is_empty()
}
pub fn vacate(&mut self, roster: &mut Roster<Key>, seat: usize) -> bool {
let Some(key) = self.seats.remove(&seat) else {
return false;
};
roster.depart(&key);
true
}
pub fn prune(&mut self, roster: &mut Roster<Key>) -> Vec<usize> {
let gone: Vec<(usize, Key)> = self
.seats
.iter()
.filter(|(seat, key)| roster.seat_of(key) != Some(**seat))
.map(|(seat, key)| (*seat, key.clone()))
.collect();
for (seat, key) in &gone {
self.seats.remove(seat);
roster.depart(key);
}
gone.into_iter().map(|(seat, _)| seat).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn bot_key(index: usize) -> u32 {
u32::MAX - index as u32
}
#[test]
fn a_crew_takes_real_seats_through_the_real_admission() {
let mut roster: Roster<u32> = Roster::new(4);
let mut crew = Crew::new();
let taken = crew.fill(&mut roster, 3, 1, bot_key);
assert_eq!(taken.len(), 3);
assert_eq!(crew.len(), 3);
for seat in &taken {
assert!(crew.holds(*seat));
}
let Admission::Seated { seat, .. } = roster.admit(7) else {
panic!("a seat was left");
};
assert!(!crew.holds(seat));
}
#[test]
fn a_full_roster_stops_the_fill_rather_than_erroring() {
let mut roster: Roster<u32> = Roster::new(2);
let mut crew = Crew::new();
let taken = crew.fill(&mut roster, 5, 1, bot_key);
assert_eq!(taken.len(), 2, "two seats existed");
assert_eq!(crew.len(), 2);
}
#[test]
fn a_person_displaces_a_bot_and_prune_reports_it() {
let mut roster: Roster<u32> = Roster::new(2).with_waitlist();
let mut crew = Crew::new();
crew.fill(&mut roster, 2, 1, bot_key);
assert!(matches!(roster.admit_ranked(7, 0), Admission::Waitlisted { .. }));
let shuffles = roster.resolve();
assert!(!shuffles.is_empty(), "the better rank moved somebody");
let gone = crew.prune(&mut roster);
assert_eq!(gone.len(), 1, "one bot lost its seat");
assert_eq!(crew.len(), 1);
assert_eq!(roster.seat_of(&7), Some(gone[0]), "and the person holds it now");
roster.depart(&7);
let shuffles = roster.resolve();
assert!(shuffles.is_empty(), "nobody was waiting: {shuffles:?}");
}
#[test]
fn a_vacated_seat_is_open_again() {
let mut roster: Roster<u32> = Roster::new(1);
let mut crew = Crew::new();
let taken = crew.fill(&mut roster, 1, 1, bot_key);
assert!(crew.vacate(&mut roster, taken[0]));
assert!(!crew.vacate(&mut roster, taken[0]), "vacating twice is not an error");
assert!(matches!(roster.admit(7), Admission::Seated { .. }));
}
#[test]
fn the_seats_come_back_sorted_whatever_order_they_were_taken() {
let mut roster: Roster<u32> = Roster::new(8);
let mut crew = Crew::new();
crew.fill(&mut roster, 6, 1, bot_key);
let seats: Vec<usize> = crew.seats().collect();
let mut sorted = seats.clone();
sorted.sort_unstable();
assert_eq!(seats, sorted);
}
}