diff --git a/src/app.rs b/src/app.rs index ca7294d..c6bcb3d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,5 @@ use crate::{ - config::Config, dbapiclient::DbApiClient, timetablepresenter::TimetablePresenter, + config::Config, dbapiclient::DbApiClient, timetableformatter::TimetableFormatter, xmlparser::XmlParser, }; @@ -7,7 +7,7 @@ pub struct App { apiclient: Box, xmlparser: Box, config: Config, - presenter: Box, + formatter: Box, } impl App { @@ -19,30 +19,36 @@ impl App { apiclient: Box, xmlparser: Box, config: Config, - presenter: Box, + formatter: Box, ) -> Self { Self { apiclient, xmlparser, config, - presenter, + formatter, } } - pub async fn run(self) { + pub async fn run(self) -> Result { + let mut ret: String = "".to_string(); for eva in &self.config.evas { - let timetable_changes = match self + let timetable = match self .apiclient .get(App::construct_changes_endpoint(eva)) .await { Ok(s) => s, - Err(_) => "".into(), + Err(e) => return Err(e.to_string()), }; - if let Ok(changes) = self.xmlparser.get_timetable(&timetable_changes) { - self.presenter.present(&changes, eva); + match self.xmlparser.get_timetable(&timetable) { + Ok(timetable) => match self.formatter.format(&timetable) { + Ok(s) => ret.push_str(&s), + Err(e) => return Err(e.to_string()), + }, + Err(e) => return Err(e.to_string()), } } + Err("".to_string()) } } diff --git a/src/config.rs b/src/config.rs index c217388..caa7d14 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,8 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Clone)] -pub enum PresenterType { - Console, +pub enum FormatterType { + SimpleString, Json, } @@ -12,7 +12,7 @@ pub struct Config { pub client_id: String, pub api_key: String, pub evas: Vec, - pub presenter: PresenterType, + pub formatter: FormatterType, } impl ::std::default::Default for Config { @@ -22,7 +22,7 @@ impl ::std::default::Default for Config { client_id: "123456789".to_string(), api_key: "123456789".to_string(), evas: vec!["8003368".to_string()], - presenter: PresenterType::Console, + formatter: FormatterType::Json, } } } diff --git a/src/dbapiclientreqwest.rs b/src/dbapiclientreqwest.rs new file mode 100644 index 0000000..7a5c2f0 --- /dev/null +++ b/src/dbapiclientreqwest.rs @@ -0,0 +1,48 @@ +use crate::{config::Config, dbapiclient::DbApiClient}; + +use reqwest::header::{HeaderMap, HeaderValue, ACCEPT}; + +pub struct DbApiClientReqwest { + config: Config, +} + +impl DbApiClientReqwest { + pub fn new(config: Config) -> Self { + Self { config } + } + + fn construct_headers(&self) -> HeaderMap { + let mut headers = HeaderMap::new(); + headers.insert( + "DB-Api-Key", + HeaderValue::from_str(self.config.api_key.as_str()).unwrap(), + ); + headers.insert( + "DB-Client-Id", + HeaderValue::from_str(self.config.client_id.as_str()).unwrap(), + ); + headers.insert(ACCEPT, HeaderValue::from_static("application/xml")); + headers + } + + fn construct_complete_url(&self, endpoint: String) -> String { + format!("{}{}", self.config.url, endpoint) + } +} + +impl DbApiClient for DbApiClientReqwest { + fn get(&self, endpoint: String) -> Result { + let client = reqwest::Client::new(); + let res = client + .get(self.construct_complete_url(endpoint)) + .headers(self.construct_headers()) + .send(); + + let body = match res.text() { + Ok(it) => it, + Err(_err) => return Err(_err), + }; + + Ok(body) + } +} diff --git a/src/main.rs b/src/main.rs index a2a5809..3ce9070 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,17 +3,17 @@ mod config; mod dbapiclient; mod dbapiclientimpl; mod timetable; -mod timetablepresenter; -mod timetablepresenterconsole; -mod timetablepresenterjson; +mod timetableformatter; +mod timetableformatterjson; +mod timetableformattersimplestring; mod xmlparser; mod xmlparserquickxml; use app::App; use config::Config; use dbapiclientimpl::DbApiClientImpl; -use timetablepresenterconsole::TimetablePresenterConsole; -use timetablepresenterjson::TimetablePresenterJson; +use timetableformatterjson::TimetableFormatterJson; +use timetableformattersimplestring::TimetableFormatterSimpleString; use xmlparserquickxml::XmlParserQuickXml; extern crate confy; @@ -28,12 +28,12 @@ fn load_config() -> Config { } } -fn create_presenter( - presentertype: &config::PresenterType, -) -> Box { - match presentertype { - config::PresenterType::Console => Box::new(TimetablePresenterConsole::new()), - config::PresenterType::Json => Box::new(TimetablePresenterJson::new()), +fn create_formatter( + formattertype: &config::FormatterType, +) -> Box { + match formattertype { + config::FormatterType::SimpleString => Box::new(TimetableFormatterSimpleString::new()), + config::FormatterType::Json => Box::new(TimetableFormatterJson::new()), } } @@ -50,9 +50,9 @@ async fn main() -> Result<(), std::io::Error> { let config = load_config(); let apiclient = create_apiclient(config.clone()); let xmlparser = create_xmlparser(); - let presenter = create_presenter(&config.presenter); - let app = App::new(apiclient, xmlparser, config.clone(), presenter); - app.run().await; - + let formatter = create_formatter(&config.formatter); + let app = App::new(apiclient, xmlparser, config.clone(), formatter); + let str_table = app.run().await; + print!("{}", str_table.unwrap_or("dbtimetable failed".to_string())); Ok(()) } diff --git a/src/timetableformatter.rs b/src/timetableformatter.rs new file mode 100644 index 0000000..c52f430 --- /dev/null +++ b/src/timetableformatter.rs @@ -0,0 +1,5 @@ +use crate::timetable::Timetable; + +pub trait TimetableFormatter { + fn format(&self, timetable: &Timetable) -> Result; +} diff --git a/src/timetablepresenterjson.rs b/src/timetableformatterjson.rs similarity index 84% rename from src/timetablepresenterjson.rs rename to src/timetableformatterjson.rs index 9653639..4e2d903 100644 --- a/src/timetablepresenterjson.rs +++ b/src/timetableformatterjson.rs @@ -1,9 +1,7 @@ use crate::timetable::{ArrivalDeparture, Timetable}; -use crate::timetablepresenter::TimetablePresenter; +use crate::timetableformatter::TimetableFormatter; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; -use std::fs::File; -use std::io::{BufWriter, Write}; #[derive(Serialize, Deserialize)] struct TimetableStop { @@ -14,21 +12,22 @@ struct TimetableStop { actual_time: String, } -pub struct TimetablePresenterJson(); +pub struct TimetableFormatterJson(); -impl TimetablePresenterJson { +impl TimetableFormatterJson { #[allow(dead_code)] pub fn new() -> Self { Self {} } } -impl TimetablePresenter for TimetablePresenterJson { - fn present(&self, timetable: &Timetable, eva: &str) { +impl TimetableFormatter for TimetableFormatterJson { + fn format(&self, timetable: &Timetable) -> Result { let station = get_station_name(timetable); let stops = get_stops(timetable, &station); let json = to_json(&stops); - write_to_file(eva, &json); + + Ok(json) } } @@ -122,10 +121,3 @@ fn to_json(stops: &Vec) -> String { let j = serde_json::to_string(&stops); j.unwrap_or_default() } - -fn write_to_file(eva: &str, json: &String) { - let filename = format!("{}.json", &eva); - let f = File::create(filename).expect("Unable to create file"); - let mut f = BufWriter::new(f); - f.write_all(json.as_bytes()).expect("Unable to write data"); -} diff --git a/src/timetableformattersimplestring.rs b/src/timetableformattersimplestring.rs new file mode 100644 index 0000000..d3e48bc --- /dev/null +++ b/src/timetableformattersimplestring.rs @@ -0,0 +1,103 @@ +use crate::timetable::{ArrivalDeparture, Timetable}; +use crate::timetableformatter::TimetableFormatter; +use chrono::NaiveDateTime; + +pub struct TimetableFormatterSimpleString(); + +impl TimetableFormatterSimpleString { + #[allow(dead_code)] + pub fn new() -> Self { + Self {} + } +} + +impl TimetableFormatter for TimetableFormatterSimpleString { + fn format(&self, timetable: &Timetable) -> Result { + let mut ret: String = "".to_string(); + ret.push_str(&station_name(timetable)); + ret.push_str(&seperator_lines(2)); + ret.push_str(&timetablestop(timetable)); + ret.push_str(&seperator_lines(1)); + + Ok(ret) + } +} + +fn timetablestop(timetable: &Timetable) -> String { + if let Some(s) = &timetable.s { + let mut ret: String = "".to_string(); + for item in s.iter() { + ret.push_str(&print_departure(item)) + } + ret + } else { + "Timetable is empty.\n".to_string() + } +} + +fn print_departure(s: &crate::timetable::TimetableStop) -> String { + let mut ret: String = "".to_string(); + if let Some(dp) = &s.dp { + if let Some(tl) = &s.tl { + ret.push_str(&train_info(tl, dp)); + ret.push_str(&time_info(dp)); + ret.push_str(&seperator_lines(1)); + } + } + ret +} + +fn time_info(dp: &ArrivalDeparture) -> String { + let mut ret: String = "".to_string(); + ret.push_str(&planned_time(dp)); + ret.push_str(&changed_time(dp)); + ret +} + +fn planned_time(dp: &ArrivalDeparture) -> String { + let pt = dp.pt.as_deref().unwrap_or("-"); + if let Ok(dt) = NaiveDateTime::parse_from_str(pt, "%y%m%d%H%M") { + return format!("Planned time: {}\n", dt); + } + "".to_string() +} + +fn changed_time(dp: &crate::timetable::ArrivalDeparture) -> String { + let ct: &str = dp.ct.as_deref().unwrap_or("-"); + match NaiveDateTime::parse_from_str(ct, "%y%m%d%H%M") { + Ok(dt) => format!("Actual time: {}\n", dt), + Err(_) => "Actual time: No delay\n".to_string(), + } +} + +fn train_info(tl: &crate::timetable::Triplabel, dp: &crate::timetable::ArrivalDeparture) -> String { + format!( + "{} {}: {}\n", + tl.c.as_ref().unwrap_or(&"".to_string()), + dp.l.as_ref().unwrap_or(&"".to_string()), + dp.ppth + .as_ref() + .unwrap_or(&"".to_string()) + .split('|') + .last() + .unwrap_or("") + ) +} + +fn station_name(timetable: &Timetable) -> String { + format!( + "{}\n", + timetable + .station + .as_ref() + .unwrap_or(&"Station name missing\n".to_string()) + ) +} + +fn seperator_lines(count: i16) -> String { + let mut ret: String = "".to_string(); + for _n in 0..count { + ret.push_str("----------------------\n"); + } + ret +} diff --git a/src/timetablepresenter.rs b/src/timetablepresenter.rs deleted file mode 100644 index 12285eb..0000000 --- a/src/timetablepresenter.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::timetable::Timetable; - -pub trait TimetablePresenter { - fn present(&self, timetable: &Timetable, eva: &str); -} diff --git a/src/timetablepresenterconsole.rs b/src/timetablepresenterconsole.rs deleted file mode 100644 index 414956d..0000000 --- a/src/timetablepresenterconsole.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::timetable::{ArrivalDeparture, Timetable}; -use crate::timetablepresenter::TimetablePresenter; -use chrono::NaiveDateTime; - -pub struct TimetablePresenterConsole(); - -impl TimetablePresenterConsole { - #[allow(dead_code)] - pub fn new() -> Self { - Self {} - } -} - -impl TimetablePresenter for TimetablePresenterConsole { - fn present(&self, timetable: &Timetable, _eva: &str) { - print_station_name(timetable); - print_seperator_lines(2); - print_timetablestop(timetable); - print_seperator_lines(1); - } -} - -fn print_timetablestop(timetable: &Timetable) { - if let Some(s) = &timetable.s { - for item in s.iter() { - print_departure(item); - } - } else { - println!("Timetable is empty."); - } -} - -fn print_departure(s: &crate::timetable::TimetableStop) { - if let Some(dp) = &s.dp { - if let Some(tl) = &s.tl { - print_train_info(tl, dp); - print_time_info(dp); - print_seperator_lines(1); - } - } -} - -fn print_time_info(dp: &ArrivalDeparture) { - print_planned_time(dp); - print_changed_time(dp); -} - -fn print_planned_time(dp: &ArrivalDeparture) { - let pt = dp.pt.as_deref().unwrap_or("-"); - if let Ok(dt) = NaiveDateTime::parse_from_str(pt, "%y%m%d%H%M") { - println!("Planned time: {}", dt); - } -} - -fn print_changed_time(dp: &crate::timetable::ArrivalDeparture) { - let ct = dp.ct.as_deref().unwrap_or("-"); - if let Ok(dt) = NaiveDateTime::parse_from_str(ct, "%y%m%d%H%M") { - println!("Actual time: {}", dt); - } else { - println!("Actual time: No delay"); - } -} - -fn print_train_info(tl: &crate::timetable::Triplabel, dp: &crate::timetable::ArrivalDeparture) { - println!( - "{} {}: {}", - tl.c.as_ref().unwrap_or(&"".to_string()), - dp.l.as_ref().unwrap_or(&"".to_string()), - dp.ppth - .as_ref() - .unwrap_or(&"".to_string()) - .split('|') - .last() - .unwrap_or("") - ); -} - -fn print_station_name(timetable: &Timetable) { - println!( - "{}", - timetable - .station - .as_ref() - .unwrap_or(&"Station name missing".to_string()) - ); -} - -fn print_seperator_lines(count: i16) { - for _n in 0..count { - println!("----------------------"); - } -}