use serde::{Deserialize, Deserializer}; use chrono::NaiveDateTime; use colored::*; use crate::serde::*; #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Station { name: String, ds100: String, uic: u64, latitude: f64, longitude: f64, #[serde(deserialize_with = "naive_read_unixtime")] scheduled_time: NaiveDateTime, #[serde(deserialize_with = "naive_read_unixtime")] real_time: NaiveDateTime, } pub fn parse_optional_station<'de, D>(d: D) -> Result, D::Error> where D: Deserializer<'de>, { let val = ::deserialize(d)?; match serde_json::from_value(val) { Ok(station) => Ok(Some(station)), Err(_) => Ok(None) } } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Stop { name: String, #[serde(deserialize_with = "option_naive_read_unixtime")] scheduled_arrival: Option, #[serde(deserialize_with = "option_naive_read_unixtime")] real_arrival: Option, #[serde(deserialize_with = "option_naive_read_unixtime")] scheduled_departure: Option, #[serde(deserialize_with = "option_naive_read_unixtime")] real_departure: Option, } pub trait IsStation { fn name (&self) -> &str; fn scheduled_arrival (&self) -> Option<&NaiveDateTime>; fn real_arrival (&self) -> Option<&NaiveDateTime>; fn ds100 (&self) -> &str; fn to_fancy_string (&self) -> String { format!( "{} {} – {} ({})", self.real_arrival().map(|t| t.time().to_string()).unwrap_or("??:??:??".to_string()).blue(), { let delay = match (self.real_arrival(), self.scheduled_arrival()) { (Some(a), Some(s)) => (a.time() - s.time()).num_minutes(), _ => 0 }; let text = format!("({:+})", delay); if delay > 0 { text.red() } else { text.green() } }, self.ds100().red(), self.name() ) } } impl IsStation for Station { fn name (&self) -> &str { &self.name } fn scheduled_arrival (&self) -> Option<&NaiveDateTime> { Some(&self.scheduled_time) } fn real_arrival (&self) -> Option<&NaiveDateTime> { Some(&self.real_time) } fn ds100 (&self) -> &str { &self.ds100 } } impl IsStation for Stop { fn name (&self) -> &str { &self.name } fn scheduled_arrival (&self) -> Option<&NaiveDateTime> { self.scheduled_arrival.as_ref() } fn real_arrival (&self) -> Option<&NaiveDateTime> { self.real_arrival.as_ref() } fn ds100 (&self) -> &str { "[??]" } } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Train { #[serde(rename = "type")] _type: String, line: Option, no: String, id: String, } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Status { deprecated: bool, checked_in: bool, from_station: Station, #[serde(deserialize_with = "parse_optional_station")] to_station: Option, intermediate_stops: Vec, train: Option, action_time: u64, } #[allow(dead_code)] pub struct Ds100 { inner: String, } pub struct Trip<'a, S: IsStation> (pub &'a Vec); impl std::fmt::Display for Train { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} {}", self._type, self.no) } } #[allow(unstable_name_collisions)] impl std::fmt::Display for Trip<'_, S> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.0.len() != 0 { self.0.iter() .map(|stop| stop.to_fancy_string()) // .intersperse(" ↓".to_string()) .for_each(|l| writeln!(f, " {}\n ↓", l).unwrap()); } Ok(()) } } impl std::fmt::Display for Status { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.checked_in { false => write!( f, "not checked in. \n\n\ last trip: \n {} {}", self.from_station.to_fancy_string(), self.to_station.as_ref().unwrap().to_fancy_string() ), true => write!( f, "checked in to: {}.\n\n\ stops:\n {}\n ↓\n{} {}", self.train.as_ref().map(|t| t.to_string()).unwrap_or("".to_string()).green(), self.from_station.to_fancy_string(), Trip(&self.intermediate_stops), self.to_station .as_ref() .map(|s| s.to_fancy_string()) .unwrap_or_else(|| "🚄 Fahrt ins Blaue".blue().to_string()) ) } } }