use crate::leg::{Leg, TripPlan};
use rustsim_modes::TravelMode;
use rustsim_transit::{RouteId, StopId, VehicleId};
#[derive(Debug, Clone)]
pub struct TravellerContext {
pub traveller_id: u64,
pub current_leg: usize,
pub state: ModeState,
}
impl TravellerContext {
pub fn new(traveller_id: u64) -> Self {
Self {
traveller_id,
current_leg: 0,
state: ModeState::Idle,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModeState {
Idle,
Walking,
Driving(TravelMode),
WaitingAt(StopId, RouteId),
RidingTransit {
vehicle: VehicleId,
alight_at: StopId,
},
OnConnector(u64),
Finished,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ModeController;
impl ModeController {
pub fn enter_current_leg(&self, ctx: &mut TravellerContext, plan: &TripPlan) {
let Some(leg) = plan.legs.get(ctx.current_leg) else {
ctx.state = ModeState::Finished;
return;
};
ctx.state = match leg {
Leg::Walk { .. } => ModeState::Walking,
Leg::DriveSelf { mode, .. } | Leg::Hail { mode, .. } => ModeState::Driving(*mode),
Leg::Transit {
route, board_at, ..
} => ModeState::WaitingAt(*board_at, *route),
Leg::Connector { connector_id } => ModeState::OnConnector(*connector_id),
};
}
pub fn complete_leg(&self, ctx: &mut TravellerContext, plan: &TripPlan) {
ctx.current_leg = ctx.current_leg.saturating_add(1);
if ctx.current_leg >= plan.legs.len() {
ctx.state = ModeState::Finished;
} else {
self.enter_current_leg(ctx, plan);
}
}
pub fn board_transit(&self, ctx: &mut TravellerContext, vehicle: VehicleId, alight_at: StopId) {
if let ModeState::WaitingAt(_, _) = ctx.state {
ctx.state = ModeState::RidingTransit { vehicle, alight_at };
}
}
pub fn alight_transit(&self, ctx: &mut TravellerContext, plan: &TripPlan) {
if matches!(ctx.state, ModeState::RidingTransit { .. }) {
self.complete_leg(ctx, plan);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::leg::Waypoint;
fn make_plan() -> TripPlan {
TripPlan {
id: 1,
legs: vec![
Leg::Walk {
from: Waypoint::ground(0.0, 0.0),
to: Waypoint::ground(5.0, 0.0),
},
Leg::Transit {
route: 42,
board_at: 10,
alight_at: 20,
},
Leg::Walk {
from: Waypoint::ground(100.0, 0.0),
to: Waypoint::ground(105.0, 0.0),
},
],
}
}
#[test]
fn traveller_advances_through_all_legs() {
let plan = make_plan();
let controller = ModeController;
let mut ctx = TravellerContext::new(7);
controller.enter_current_leg(&mut ctx, &plan);
assert_eq!(ctx.state, ModeState::Walking);
controller.complete_leg(&mut ctx, &plan);
assert_eq!(ctx.state, ModeState::WaitingAt(10, 42));
controller.board_transit(&mut ctx, 99, 20);
assert_eq!(
ctx.state,
ModeState::RidingTransit {
vehicle: 99,
alight_at: 20
}
);
controller.alight_transit(&mut ctx, &plan);
assert_eq!(ctx.state, ModeState::Walking);
controller.complete_leg(&mut ctx, &plan);
assert_eq!(ctx.state, ModeState::Finished);
}
}