/*!
* A timetable vocabulary.
*
* Copyright (C) 2023-2024 kaoru
*/
use std::collections::HashMap;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::io::{BufRead, Lines};
use std::rc::Rc;
use anyhow::Result;
use tetengo_lattice::{Entry, EntryView, HashMapVocabulary, StringInput, Vocabulary};
/**
* A timetable error.
*/
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub(crate) enum TimetableError {
/**
* Unexpected end of file.
*/
#[error("unexpected end of file")]
UnexpectedEndOfFile,
/**
* Station names and telegram codes unmatch.
*/
#[error("station names and telegram codes unmatch")]
StationNamesAndTelegramCodesUnmatch,
/**
* Invalid train line found.
*/
#[error("invalid train line found")]
InvalidTrainLineFound,
/**
* Invalid arrival/departure time found.
*/
#[error("invalid arrival/departure time found")]
InvalidArrivalOrDepartureTimeFound,
/**
* Invalid time found.
*/
#[error("invalid time found")]
InvalidTimeFound,
/**
* Both arrival and departure time not found.
*/
#[error("both arrival and departure time not found")]
BothArrivalAndDepartureTimeNotFound,
}
/**
* A station.
*/
#[derive(Debug)]
pub(crate) struct Station {
name: String,
telegram_code: String,
}
impl Station {
/**
* Creates a station.
*
* # Arguments
* * `name` - A name.
* * `telegram_code` - A telegram code.
*/
pub(crate) const fn new(name: String, telegram_code: String) -> Self {
Self {
name,
telegram_code,
}
}
/**
* Returns the name.
*
* # Returns
* The name.
*/
pub(crate) fn name(&self) -> &str {
self.name.as_str()
}
/**
* Returns the telegram code.
*
* # Returns
* The telegram code.
*/
pub(crate) fn telegram_code(&self) -> &str {
self.telegram_code.as_str()
}
}
/**
* A stop.
*/
#[derive(Clone, Debug)]
pub(crate) struct Stop {
arrival_time: Option,
departure_time: Option,
}
impl Stop {
/**
* Creates a stop.
*
* # Arguments
* * `arrival_time` - An arrival time.
* * `departure_time` - A departure time.
*/
pub(crate) const fn new(arrival_time: Option, departure_time: Option) -> Self {
Self {
arrival_time,
departure_time,
}
}
/**
* Returns the arrival time.
*
* # Returns
* The arrival time.
*/
pub(crate) const fn arrival_time(&self) -> Option {
self.arrival_time
}
/**
* Sets an arrival time.
*
* # Arguments
* * `time` - An arrival time.
*/
pub(crate) fn set_arrival_time(&mut self, time: usize) {
self.arrival_time = Some(time);
}
/**
* Returns the departure time.
*
* # Returns
* The departure time.
*/
pub(crate) const fn departure_time(&self) -> Option {
self.departure_time
}
/**
* Sets a departure time.
*
* # Arguments
* * `time` - A departure time.
*/
pub(crate) fn set_departure_time(&mut self, time: usize) {
self.departure_time = Some(time);
}
}
/**
* A train.
*/
#[derive(Clone, Debug)]
pub(crate) struct Train {
number: String,
name: String,
stops: Vec,
}
impl Train {
/**
* Creates a train.
*
* # Arguments
* * `number` - A number.
* * `name` - A name.
* * `stops` - Stops.
*/
pub(crate) const fn new(number: String, name: String, stops: Vec) -> Self {
Self {
number,
name,
stops,
}
}
/**
* Returns the number.
*
* # Returns
* The number.
*/
pub(crate) fn number(&self) -> &str {
self.number.as_str()
}
/**
* Returns the name.
*
* # Returns
* The name.
*/
pub(crate) fn name(&self) -> &str {
self.name.as_str()
}
/**
* Returns the stops.
*
* # Returns
* The stops.
*/
pub(crate) fn stops(&self) -> &[Stop] {
self.stops.as_slice()
}
/**
* Returns the stops.
*
* # Returns
* The stops.
*/
pub(crate) fn stops_mut(&mut self) -> &mut Vec {
&mut self.stops
}
}
/**
* A section.
*/
#[derive(Clone, Debug)]
pub(crate) struct Section {
train: Rc,
from: usize,
to: usize,
}
impl Section {
/**
* Creates a section.
*
* # Arguments
* * `train` - A train.
* * `from` - A departure station index.
* * `to` - An arrival station index.
*/
pub(crate) const fn new(train: Rc, from: usize, to: usize) -> Self {
Self { train, from, to }
}
/**
* Returns the train.
*
* # Returns
* The train.
*/
pub(crate) fn train(&self) -> &Train {
self.train.as_ref()
}
/**
* Returns the departure station index.
*
* # Returns
* The departure station index.
*/
pub(crate) const fn from(&self) -> usize {
self.from
}
/**
* Returns the arrival station index.
*
* # Returns
* The arrival station index.
*/
pub(crate) const fn to(&self) -> usize {
self.to
}
}
#[derive(Debug)]
struct TimetableValue {
stations: Vec,
trains: Vec,
}
impl TimetableValue {
const fn new(stations: Vec, trains: Vec) -> Self {
Self { stations, trains }
}
}
/**
* A timetable vocabulary.
*/
#[derive(Debug)]
pub(crate) struct Timetable {
value: TimetableValue,
}
impl Timetable {
/**
* Creates a timetable vocabulary.
*
* # Arguments
* * `reader` - A reader.
*/
pub(crate) fn new(reader: Box) -> Result {
Ok(Self {
value: Self::build_timetable(reader)?,
})
}
fn build_timetable(mut reader: Box) -> Result {
let mut value = Self::parse_input(reader.as_mut())?;
Self::guess_arrival_times(&mut value)?;
Ok(value)
}
fn parse_input(reader: &mut dyn BufRead) -> Result {
let mut lines = reader.lines();
let stations = {
let Some(line1) = Self::read_line(&mut lines)? else {
return Err(TimetableError::UnexpectedEndOfFile.into());
};
let Some(line2) = Self::read_line(&mut lines)? else {
return Err(TimetableError::UnexpectedEndOfFile.into());
};
Self::parse_stations(line1, line2)?
};
let trains = {
let mut trains = Vec::new();
while let Some(line) = Self::read_line(&mut lines)? {
if line.is_empty() || (line.len() == 1 && line[0].is_empty()) {
continue;
}
trains.push(Self::parse_train(line, stations.len())?);
}
trains
};
Ok(TimetableValue::new(stations, trains))
}
fn read_line(lines: &mut Lines<&mut dyn BufRead>) -> Result