use rota_lib::{ config::{ToxicPair, UserConfig}, schedule::MonthlySchedule, slot::{Day, ShiftPosition, Slot}, workload::{WorkloadBounds, WorkloadTracker}, }; pub fn validate_all_constraints( schedule: &MonthlySchedule, tracker: &WorkloadTracker, config: &UserConfig, ) { assert_eq!(schedule.0.len() as u8, config.total_slots); for d in 2..=config.total_days { let current: Vec<_> = [ShiftPosition::First, ShiftPosition::Second] .iter() .filter_map(|&p| schedule.get_resident_id(&Slot::new(Day(d), p))) .collect(); let previous: Vec<_> = [ShiftPosition::First, ShiftPosition::Second] .iter() .filter_map(|&p| schedule.get_resident_id(&Slot::new(Day(d - 1), p))) .collect(); for r in current { assert!(!previous.contains(&r)); } } for d in 1..=config.total_days { let day = Day(d); if day.is_open_shift() { let r1 = schedule.get_resident_id(&Slot::new(day, ShiftPosition::First)); let r2 = schedule.get_resident_id(&Slot::new(day, ShiftPosition::Second)); assert_ne!(r1, r2); if let (Some(id1), Some(id2)) = (r1, r2) { let pair = ToxicPair::from((*id1, *id2)); assert!(config.toxic_pairs.iter().all(|t| !t.matches(&pair))); } } } let bounds = WorkloadBounds::new_with_config(config); for (slot, r_id) in &schedule.0 { let r = config .residents .iter() .find(|r| &r.id == r_id) .expect("Resident not found"); assert!(r.allowed_types.contains(&slot.shift_type())); assert!(!r.negative_shifts.contains(&slot.day)); } for resident in &config.residents { let workload = tracker.current_workload(&resident.id); let max = *bounds.max_workloads.get(&resident.id).unwrap(); assert!(workload <= max, "workload: {}, max: {}", workload, max); } }