summaryrefslogtreecommitdiff
path: root/src/types.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/types.rs')
-rw-r--r--src/types.rs208
1 files changed, 208 insertions, 0 deletions
diff --git a/src/types.rs b/src/types.rs
new file mode 100644
index 0000000..3dcb6db
--- /dev/null
+++ b/src/types.rs
@@ -0,0 +1,208 @@
+use serde::{Deserialize, Deserializer};
+
+use chrono::NaiveDateTime;
+use colored::*;
+use itertools::Itertools;
+
+#[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<Option<Station>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ let val = <serde_json::Value>::deserialize(d)?;
+ match serde_json::from_value(val) {
+ Ok(station) => Ok(Some(station)),
+ Err(_) => Ok(None)
+ }
+}
+
+
+fn naive_read_unixtime<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ let ts = <i64>::deserialize(d)?;
+ Ok(NaiveDateTime::from_timestamp(ts, 0))
+}
+fn option_naive_read_unixtime<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ match <i64>::deserialize(d) {
+ Ok(ts) =>
+ Ok(Some(NaiveDateTime::from_timestamp(ts, 0))),
+ 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<NaiveDateTime>,
+ #[serde(deserialize_with = "option_naive_read_unixtime")]
+ real_arrival: Option<NaiveDateTime>,
+ #[serde(deserialize_with = "option_naive_read_unixtime")]
+ scheduled_departure: Option<NaiveDateTime>,
+ #[serde(deserialize_with = "option_naive_read_unixtime")]
+ real_departure: Option<NaiveDateTime>,
+}
+
+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<String>,
+ 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<Station>,
+ intermediate_stops: Vec<Stop>,
+ train: Option<Train>,
+ action_time: u64,
+}
+
+#[allow(dead_code)]
+pub struct Ds100 {
+ inner: String,
+}
+
+struct Trip<'a> (&'a Vec<Stop>);
+
+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<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ if self.0.len() == 0 {
+ write!(f, "(none)")
+ } else {
+ self.0.iter()
+ .map(|stop| stop.to_fancy_string())
+ .intersperse(" ↓".to_string())
+ .for_each(|l| writeln!(f, " {}", 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 {}\n ↓\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{} ↓\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())
+ )
+ }
+ }
+}