summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs26
-rw-r--r--src/zugportal.rs97
3 files changed, 123 insertions, 1 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 4172b8a..4107efe 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,3 +2,4 @@ pub mod iceportal;
pub(crate) mod serde;
pub mod travelynx;
pub mod types;
+pub mod zugportal;
diff --git a/src/main.rs b/src/main.rs
index 1782953..7e1c85a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -41,7 +41,8 @@ enum Command {
/// Undo the last checkin (if any).
Undo,
/// Query iceportal.de (for testing)
- ICEPortal
+ ICEPortal,
+ Zugportal
}
#[derive(Deserialize)]
@@ -213,6 +214,29 @@ fn main() -> Result<(), ureq::Error> {
}
}
}
+ Command::Zugportal => {
+ match get_request::<traveltext::zugportal::Journey>(
+ // "https://iceportal.de/api1/rs/tripInfo/trip"
+ "https://zugportal.de/prd/zupo-travel-information/api/public/ri/journey"
+ ) {
+ Ok(resp) => {
+ println!(
+ "{}: Currently in {}\n",
+ traveltext,
+ resp.get_train_ref().to_string().green()
+ );
+ // println!("guessing last stop was: {:?}\n", resp.guess_last_station());
+ println!("Stops:\n{}", resp.trip())
+ }
+ Err(err) => {
+ if cli.debug {
+ eprintln!("{:?}", err);
+ }
+ println!("either this tool or the zugportal broke or you're not actually on an ICE\n\
+ (get a response but couldn't parse it)");
+ }
+ }
+ }
}
Ok(())
}
diff --git a/src/zugportal.rs b/src/zugportal.rs
new file mode 100644
index 0000000..f00cd04
--- /dev/null
+++ b/src/zugportal.rs
@@ -0,0 +1,97 @@
+/// implementation of traits to query zugportal.de
+/// (available at least in the Munich S-Bahn, maybe other trains)
+
+use chrono::{DateTime, Utc};
+use serde::Deserialize;
+use serde_json::Value;
+
+use crate::{serde::*, travelynx::TrainRef, types::IsStation};
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all="camelCase")]
+pub struct Journey {
+ name: String, // the line's name, e.g. S 8
+ no: i64,
+ stops: Vec<Stop>
+}
+
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all="camelCase")]
+pub struct Stop {
+ station: Station,
+ status: String, // one of "Normal", ...?
+ track: Track,
+ messages: Vec<String>,
+ arrival_time: Option<DepartureTime>,
+ departure_time: Option<DepartureTime>
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all="camelCase")]
+struct Station {
+ eva_no: String,
+ name: String
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all="camelCase")]
+struct Track {
+ target: String,
+ prediction: String
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all="camelCase")]
+struct DepartureTime {
+ target: DateTime<Utc>,
+ predicted: DateTime<Utc>,
+ time_type: String, // one of REAL, PREVIEW, ..?
+ diff: i64, // diff in minutes?
+ // NOTE: also sends predictedTimeInMs and targetTimeInMs; these might be unix times
+}
+
+impl IsStation for Stop {
+ fn name(&self) -> &str {
+ &self.station.name
+ }
+
+ fn scheduled_arrival(&self) -> Option<&chrono::DateTime<Utc>> {
+ self.arrival_time.as_ref().map(|t| &t.target)
+ }
+
+ fn real_arrival(&self) -> Option<&chrono::DateTime<Utc>> {
+ self.arrival_time.as_ref().map(|t| &t.predicted)
+ }
+
+ fn ds100(&self) -> &str {
+ "??"
+ }
+}
+
+impl Journey {
+ pub fn guess_last_station(&self) -> Option<String> {
+ todo!()
+ // let current_pos = self.trip.actual_position;
+ // self
+ // .trip
+ // .stops
+ // .iter()
+ // .rev()
+ // .map(|stop| (stop.info.distance_from_start, stop))
+ // .filter(|(dist, _)| dist <= &current_pos)
+ // .next()
+ // .map(|(_, stop)| stop.station.name.clone())
+ }
+
+ pub fn get_train_ref(&self) -> TrainRef {
+ TrainRef {
+ _type: self.name.clone(),
+ no: self.no.to_string().clone()
+ }
+ }
+
+ pub fn trip(&self) -> crate::types::Trip<'_, Stop> {
+ crate::types::Trip(&self.stops)
+ }
+}