rustsim_transit/
schedule.rs1pub type Departure = f64;
5
6#[derive(Debug, Clone)]
8pub enum Schedule {
9 FixedHeadway {
11 headway: f64,
13 first: f64,
15 },
16 Timetable(Vec<Departure>),
18}
19
20impl Schedule {
21 pub fn fixed_headway(headway: f64) -> Self {
23 Self::FixedHeadway {
24 headway,
25 first: 0.0,
26 }
27 }
28
29 pub fn fixed_headway_starting(headway: f64, first: f64) -> Self {
31 Self::FixedHeadway { headway, first }
32 }
33
34 pub fn timetable(departures: Vec<Departure>) -> Self {
36 Self::Timetable(departures)
37 }
38
39 pub fn next_departure(&self, now: f64) -> Option<Departure> {
41 match self {
42 Schedule::FixedHeadway { headway, first } => {
43 if *headway <= 0.0 {
44 return None;
45 }
46 let elapsed = (now - first).max(0.0);
47 let k = (elapsed / headway).ceil();
48 Some(first + k * headway)
49 }
50 Schedule::Timetable(ts) => ts.iter().copied().find(|t| *t >= now),
51 }
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn fixed_headway_alignment() {
61 let s = Schedule::fixed_headway(300.0);
62 assert_eq!(s.next_departure(0.0), Some(0.0));
63 assert_eq!(s.next_departure(100.0), Some(300.0));
64 assert_eq!(s.next_departure(600.0), Some(600.0));
65 }
66
67 #[test]
68 fn timetable_picks_next_after_now() {
69 let s = Schedule::timetable(vec![100.0, 200.0, 350.0]);
70 assert_eq!(s.next_departure(50.0), Some(100.0));
71 assert_eq!(s.next_departure(150.0), Some(200.0));
72 assert_eq!(s.next_departure(400.0), None);
73 }
74}