use std::time::Duration; use chrono::Local; use reqwest::StatusCode; use serde::Serialize; use tokio::time::sleep; use crate::traewelling::{ model::{JsonableData, Status, StopJourneyPart}, RequestErr, TraewellingClient, }; pub async fn get_current_journey() -> anyhow::Result<()> { let client = TraewellingClient::new()?; let mut state; let mut cur_active_checkin = None; loop { match client.get_active_checkin().await { Ok(status) => { cur_active_checkin = Some(status); state = State::Live; } Err(err) => { if err == RequestErr::WithStatus(StatusCode::NOT_FOUND) { state = State::NoCheckin; cur_active_checkin = None; } else { state = State::NoConnectionOrSomethingElseDoesntWork; } } }; match (state, &cur_active_checkin) { (State::Live | State::NoConnectionOrSomethingElseDoesntWork, Some(status)) => { let live = state == State::Live; let out = CurrentJourneyOutput::new(&status, live); println!( "{}", serde_json::to_string(&out) .expect("serde should not make you sad but it does because it's serde") ); sleep(Duration::from_secs(20)).await; } (_, None) | (State::NoCheckin, Some(_)) => { println!("null"); sleep(Duration::from_secs(60)).await; } } } Ok(()) } #[derive(PartialEq, Eq, Clone, Copy, Debug)] enum State { Live, NoConnectionOrSomethingElseDoesntWork, NoCheckin, } #[derive(Serialize)] struct CurrentJourneyOutput { live: bool, // Journey progress, 0.0-1.0 progress: Option, time_left: Option, icon: String, line: String, // Invalid data received? departure_err: bool, departure_planned: Option, departure_real: Option, departure_station: String, departure_ril100: Option, departure_platform_data_available: bool, departure_platform_planned: Option, departure_platform_real: Option, // Invalid data received? arrival_err: bool, arrival_planned: Option, arrival_real: Option, arrival_station: String, arrival_ril100: Option, arrival_platform_data_available: bool, arrival_platform_planned: Option, arrival_platform_real: Option, } impl CurrentJourneyOutput { fn new(checkin: &Status, live: bool) -> Self { let JsonableData { time_err: departure_err, time_planned: departure_planned, time_real: departure_real, station: departure_station, ril100: departure_ril100, platform_data_available: departure_platform_data_available, platform_planned: departure_platform_planned, platform_real: departure_platform_real, } = checkin.train.origin.get_time_data(StopJourneyPart::Origin); let JsonableData { time_err: arrival_err, time_planned: arrival_planned, time_real: arrival_real, station: arrival_station, ril100: arrival_ril100, platform_data_available: arrival_platform_data_available, platform_planned: arrival_platform_planned, platform_real: arrival_platform_real, } = checkin .train .destination .get_time_data(StopJourneyPart::Destination); let (progress, time_left) = if !departure_err && !arrival_err { let departure = departure_real.unwrap_or(departure_planned.unwrap()); let arrival = arrival_real.unwrap_or(arrival_planned.unwrap()); let dur = arrival - departure; let now = Local::now().timestamp(); let progress = ((now - departure) as f32) / dur as f32; let time_left = arrival - now; (Some(progress), Some(time_left)) } else { (None, None) }; let icon = match checkin.train.category.as_str() { "nationalExpress" | "national" => "longDistanceTrans", "regionalExp" | "regional" => "regionalTrans", "suburban" => "localTrans", "subway" => "subTrans", "bus" => "bus", "tram" => "tram", "ferry" => "ferry", _ => "other", } .to_string(); CurrentJourneyOutput { live, progress, time_left, icon, line: checkin.train.line_name.clone(), departure_err, departure_planned, departure_real, departure_station, departure_ril100, departure_platform_data_available, departure_platform_planned, departure_platform_real, arrival_err, arrival_planned, arrival_real, arrival_station, arrival_ril100, arrival_platform_data_available, arrival_platform_planned, arrival_platform_real, } } } enum TransportType { // FV, ob jetzt NJ, IC, ICE... egal LongDistanceTrans, RegionalTrans, // S-bahn... LocalTrans, // U-bahn SubTrans, Bus, Tram, Ferry, }