rustsim_mobility/
mode_state.rs1use crate::leg::{Leg, TripPlan};
9use rustsim_modes::TravelMode;
10use rustsim_transit::{RouteId, StopId, VehicleId};
11
12#[derive(Debug, Clone)]
14pub struct TravellerContext {
15 pub traveller_id: u64,
17 pub current_leg: usize,
19 pub state: ModeState,
21}
22
23impl TravellerContext {
24 pub fn new(traveller_id: u64) -> Self {
26 Self {
27 traveller_id,
28 current_leg: 0,
29 state: ModeState::Idle,
30 }
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum ModeState {
37 Idle,
39 Walking,
41 Driving(TravelMode),
43 WaitingAt(StopId, RouteId),
45 RidingTransit {
47 vehicle: VehicleId,
49 alight_at: StopId,
51 },
52 OnConnector(u64),
54 Finished,
56}
57
58#[derive(Debug, Clone, Copy, Default)]
60pub struct ModeController;
61
62impl ModeController {
63 pub fn enter_current_leg(&self, ctx: &mut TravellerContext, plan: &TripPlan) {
66 let Some(leg) = plan.legs.get(ctx.current_leg) else {
67 ctx.state = ModeState::Finished;
68 return;
69 };
70 ctx.state = match leg {
71 Leg::Walk { .. } => ModeState::Walking,
72 Leg::DriveSelf { mode, .. } | Leg::Hail { mode, .. } => ModeState::Driving(*mode),
73 Leg::Transit {
74 route, board_at, ..
75 } => ModeState::WaitingAt(*board_at, *route),
76 Leg::Connector { connector_id } => ModeState::OnConnector(*connector_id),
77 };
78 }
79
80 pub fn complete_leg(&self, ctx: &mut TravellerContext, plan: &TripPlan) {
82 ctx.current_leg = ctx.current_leg.saturating_add(1);
83 if ctx.current_leg >= plan.legs.len() {
84 ctx.state = ModeState::Finished;
85 } else {
86 self.enter_current_leg(ctx, plan);
87 }
88 }
89
90 pub fn board_transit(&self, ctx: &mut TravellerContext, vehicle: VehicleId, alight_at: StopId) {
93 if let ModeState::WaitingAt(_, _) = ctx.state {
94 ctx.state = ModeState::RidingTransit { vehicle, alight_at };
95 }
96 }
97
98 pub fn alight_transit(&self, ctx: &mut TravellerContext, plan: &TripPlan) {
100 if matches!(ctx.state, ModeState::RidingTransit { .. }) {
101 self.complete_leg(ctx, plan);
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::leg::Waypoint;
110
111 fn make_plan() -> TripPlan {
112 TripPlan {
113 id: 1,
114 legs: vec![
115 Leg::Walk {
116 from: Waypoint::ground(0.0, 0.0),
117 to: Waypoint::ground(5.0, 0.0),
118 },
119 Leg::Transit {
120 route: 42,
121 board_at: 10,
122 alight_at: 20,
123 },
124 Leg::Walk {
125 from: Waypoint::ground(100.0, 0.0),
126 to: Waypoint::ground(105.0, 0.0),
127 },
128 ],
129 }
130 }
131
132 #[test]
133 fn traveller_advances_through_all_legs() {
134 let plan = make_plan();
135 let controller = ModeController;
136 let mut ctx = TravellerContext::new(7);
137 controller.enter_current_leg(&mut ctx, &plan);
138 assert_eq!(ctx.state, ModeState::Walking);
139
140 controller.complete_leg(&mut ctx, &plan);
141 assert_eq!(ctx.state, ModeState::WaitingAt(10, 42));
142
143 controller.board_transit(&mut ctx, 99, 20);
144 assert_eq!(
145 ctx.state,
146 ModeState::RidingTransit {
147 vehicle: 99,
148 alight_at: 20
149 }
150 );
151
152 controller.alight_transit(&mut ctx, &plan);
153 assert_eq!(ctx.state, ModeState::Walking);
154
155 controller.complete_leg(&mut ctx, &plan);
156 assert_eq!(ctx.state, ModeState::Finished);
157 }
158}