/// implementation of traits to query the iceportal.de /// (available in high speed trains in DE) use chrono::{DateTime, Utc}; use serde::Deserialize; use serde_json::Value; use crate::onboard; use crate::onboard::{OnBoardAPI, OnBoardInfo}; use crate::{serde::*, traits::*, travelynx::TrainRef}; use crate::types::Trip; pub struct Iceportal {} #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct TripInfo { trip: IceTrip, connection: Option, selected_route: Option, active: Option } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct IceTrip { train_type: String, vzn: String, // train number // some position info here actual_position: u64, // distance along track, presumably stops: Vec } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Stop { info: StopInfo, station: Station, timetable: Timetable } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct StopInfo { distance_from_start: u64, position_status: Option // one of "departed", "future", ... (?); null if cancelled } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct Station { eva_nr: String, name: String } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct Timetable { #[serde(deserialize_with = "option_naive_read_unixtime_db")] scheduled_arrival_time: Option>, #[serde(deserialize_with = "option_naive_read_unixtime_db")] actual_arrival_time: Option> } impl IsStation for Stop { fn name(&self) -> &str { &self.station.name } fn scheduled_arrival(&self) -> Option<&chrono::DateTime> { self.timetable.scheduled_arrival_time.as_ref() } fn real_arrival(&self) -> Option<&chrono::DateTime> { self.timetable.scheduled_arrival_time.as_ref() } fn ds100(&self) -> Option<&str> { None } } impl OnBoardInfo for TripInfo { fn guess_last_station(&self) -> Option<&dyn IsStation> { let current_pos = self.trip.actual_position; self .trip .stops .iter() .rev() .filter(|stop| stop.info.position_status.is_some()) .map(|stop| (stop.info.distance_from_start, stop)) .filter(|(dist, _)| dist <= ¤t_pos) .next() .map(|(_, stop)| stop as &dyn IsStation) } fn get_train_ref(&self) -> TrainRef { TrainRef { _type: self.trip.train_type.clone(), no: self.trip.vzn.clone() } } fn stops<'a>(&'a self) -> Trip<'a> { (&self.trip.stops).into() } } impl std::fmt::Display for Stop { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.station.name) } } impl OnBoardAPI for Iceportal { fn apiurl(&self) -> &'static str { "https://iceportal.de/api1/rs/tripInfo/trip" } fn request( &self, debug: bool ) -> Result, serde_json::Error> { onboard::request::<_, TripInfo>(self, debug) } }