From ffcc319650a5bb26aab8e784c2583bd84f9ad82b Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Thu, 5 Jan 2023 14:16:39 +0100 Subject: [PATCH] add number stops from args --- src/args.rs | 25 ++++++++++--- src/lib.rs | 2 +- src/main.rs | 8 ++-- src/query.rs | 97 ++++++++++++++++++++++++++++++++++--------------- src/response.rs | 10 ++--- 5 files changed, 98 insertions(+), 44 deletions(-) diff --git a/src/args.rs b/src/args.rs index 8840ed4..a93c4b3 100644 --- a/src/args.rs +++ b/src/args.rs @@ -2,9 +2,24 @@ use std::env::args; use anyhow::{Context, Result}; -pub fn get_station_name_from_args() -> Result { - args() - .into_iter() - .nth(1) - .context("getting station name from args") +pub struct Args { + pub name: String, + pub number_stops: usize, +} + +impl Args { + pub fn try_get() -> Result { + let mut iter = args().into_iter().skip(1); + let name = iter.next().context("getting station name")?; + let number_stops = get_number_stops(iter.next()).context("getting number of stops")?; + + Ok(Self { name, number_stops }) + } +} + +fn get_number_stops(arg: Option) -> Result { + match arg { + Some(s) => s.parse().context("parsing number"), + None => Ok(5), + } } diff --git a/src/lib.rs b/src/lib.rs index 7814c44..55996c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,6 @@ mod args; mod query; pub mod response; -pub use args::get_station_name_from_args; +pub use args::Args; pub use query::get_departures_for_station; pub use response::format_departures; diff --git a/src/main.rs b/src/main.rs index e0a1a2b..173c730 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ -use anyhow::Result; +use anyhow::{Context, Result}; -use departure::{format_departures, get_departures_for_station, get_station_name_from_args}; +use departure::{format_departures, get_departures_for_station, Args}; fn main() -> Result<()> { - let station_name = get_station_name_from_args()?; - let response = get_departures_for_station(station_name)?; + let args = Args::try_get().context("parsing args")?; + let response = get_departures_for_station(args).context("getting departures")?; let formatted = format_departures(response); println!("{formatted}"); Ok(()) diff --git a/src/query.rs b/src/query.rs index 5bf3ece..169414f 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,52 +1,91 @@ -use anyhow::Result; -use ureq::Request; +use std::collections::HashMap; -use crate::response::{PointFinderResponse, Response}; +use anyhow::{bail, Context, Result}; + +use crate::{ + response::{PointFinderResponse, Response}, + Args, +}; const DEPARTURE_MONITOR: &str = "https://webapi.vvo-online.de/dm"; const POINT_FINDER: &str = "https://webapi.vvo-online.de/tr/pointfinder"; -pub fn get_departures_for_station(station_name: String) -> Result { - let stopid = get_stopid_from_station_name(station_name)?; +pub fn get_departures_for_station(args: Args) -> Result { + let stopid = match get_stopid_from_station_name(args.name) { + Ok(value) => value, + Err(e) => bail!( + "could not resolve stop ID. Check the spelling of the stop name\nserde error: {e}" + ), + }; - let json_string = create_json_request(DEPARTURE_MONITOR) - .send_string(&format!( - "{{ - \"stopid\":\"{stopid}\", - \"limit\":5 - }}" - ))? - .into_string()?; + let number_stops = args.number_stops.to_string(); + let kv_pair = get_key_value_pair_for_departure(&stopid, &number_stops); + + let json_string = create_json_request(DEPARTURE_MONITOR, kv_pair) + .context("creating JSON request")? + .into_string() + .context("converting JSON reply to string")?; let result = serde_json::from_str(&json_string)?; Ok(result) } fn get_stopid_from_station_name(station_name: String) -> Result { - let json_string = create_json_request(POINT_FINDER) - .send_string(&format!( - "{{ - \"query\":\"{station_name}\", - \"limit\":1, - \"regionalOnly\":\"true\", - \"stopsOnly\":\"true\" - }}" - ))? - .into_string()?; + let kv_pair = get_key_value_pair_for_stopid(&station_name); + + let json_string = create_json_request(POINT_FINDER, kv_pair) + .context("creating JSON request")? + .into_string() + .context("converting JSON reply to string")?; let response: PointFinderResponse = serde_json::from_str(&json_string)?; - print!("{}", response.point.name); - if let Some(area) = response.point.area { - print!(", {area}"); - } - println!("\n"); + print_station_name(&response); let result = response.point.stopid; Ok(result) } -fn create_json_request(url: &str) -> Request { +fn print_station_name(response: &PointFinderResponse) { + print!("{}", response.point.name); + let area = &response.point.area; + if !area.is_empty() { + print!(", {area}"); + } + println!("\n"); +} + +fn create_json_request(url: &str, kv_pair: HashMap<&str, &str>) -> Result { ureq::post(url) .set("Content-Type", "application/json") .set("charset", "UTF-8") + .send_string(&create_json_body(kv_pair)) + .context("sending JSON to server") +} + +fn create_json_body(kv_pair: HashMap<&str, &str>) -> String { + let mut result = String::from("{"); + for (k, v) in kv_pair { + result.push_str(&format!("\"{k}\":\"{v}\",")) + } + result.push('}'); + result +} + +fn get_key_value_pair_for_departure<'a>( + stopid: &'a str, + number_stops: &'a str, +) -> HashMap<&'a str, &'a str> { + let mut result = HashMap::new(); + result.insert("stopid", stopid); + result.insert("limit", number_stops); + result +} + +fn get_key_value_pair_for_stopid(name: &str) -> HashMap<&str, &str> { + let mut result = HashMap::new(); + result.insert("query", name); + result.insert("limit", "1"); + result.insert("regionalOnly", "true"); + result.insert("stopsOnly", "true"); + result } diff --git a/src/response.rs b/src/response.rs index e5b4783..1a3f955 100644 --- a/src/response.rs +++ b/src/response.rs @@ -85,15 +85,15 @@ pub struct PointFinderResponse { pub struct Point { pub stopid: String, pub name: String, - pub area: Option, + pub area: String, } -impl From<(&str, &str, Option<&str>)> for Point { - fn from(value: (&str, &str, Option<&str>)) -> Self { +impl From<(&str, &str, &str)> for Point { + fn from(value: (&str, &str, &str)) -> Self { Self { stopid: value.0.into(), name: value.1.into(), - area: value.2.map(|s| s.into()), + area: value.2.into(), } } } @@ -104,7 +104,7 @@ fn extract_data_from_response(line: &str) -> Result { let mut iter = line.trim_start_matches("\\\"").split_terminator('|'); let stopid = iter.next().context("getting stopid from line")?; - let area = iter.nth(1).context("getting area from line").ok(); + let area = iter.nth(1).context("getting area from line")?; let name = iter.next().context("getting stop name from line")?; Ok((stopid, name, area).into()) }