add number stops from args

This commit is contained in:
Dr. Matthias Ratajczak
2023-01-05 14:16:39 +01:00
parent 65881f7174
commit ffcc319650
5 changed files with 98 additions and 44 deletions
+20 -5
View File
@@ -2,9 +2,24 @@ use std::env::args;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
pub fn get_station_name_from_args() -> Result<String> { pub struct Args {
args() pub name: String,
.into_iter() pub number_stops: usize,
.nth(1) }
.context("getting station name from args")
impl Args {
pub fn try_get() -> Result<Self> {
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<String>) -> Result<usize> {
match arg {
Some(s) => s.parse().context("parsing number"),
None => Ok(5),
}
} }
+1 -1
View File
@@ -2,6 +2,6 @@ mod args;
mod query; mod query;
pub mod response; pub mod response;
pub use args::get_station_name_from_args; pub use args::Args;
pub use query::get_departures_for_station; pub use query::get_departures_for_station;
pub use response::format_departures; pub use response::format_departures;
+4 -4
View File
@@ -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<()> { fn main() -> Result<()> {
let station_name = get_station_name_from_args()?; let args = Args::try_get().context("parsing args")?;
let response = get_departures_for_station(station_name)?; let response = get_departures_for_station(args).context("getting departures")?;
let formatted = format_departures(response); let formatted = format_departures(response);
println!("{formatted}"); println!("{formatted}");
Ok(()) Ok(())
+68 -29
View File
@@ -1,52 +1,91 @@
use anyhow::Result; use std::collections::HashMap;
use ureq::Request;
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 DEPARTURE_MONITOR: &str = "https://webapi.vvo-online.de/dm";
const POINT_FINDER: &str = "https://webapi.vvo-online.de/tr/pointfinder"; const POINT_FINDER: &str = "https://webapi.vvo-online.de/tr/pointfinder";
pub fn get_departures_for_station(station_name: String) -> Result<Response> { pub fn get_departures_for_station(args: Args) -> Result<Response> {
let stopid = get_stopid_from_station_name(station_name)?; 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) let number_stops = args.number_stops.to_string();
.send_string(&format!( let kv_pair = get_key_value_pair_for_departure(&stopid, &number_stops);
"{{
\"stopid\":\"{stopid}\", let json_string = create_json_request(DEPARTURE_MONITOR, kv_pair)
\"limit\":5 .context("creating JSON request")?
}}" .into_string()
))? .context("converting JSON reply to string")?;
.into_string()?;
let result = serde_json::from_str(&json_string)?; let result = serde_json::from_str(&json_string)?;
Ok(result) Ok(result)
} }
fn get_stopid_from_station_name(station_name: String) -> Result<String> { fn get_stopid_from_station_name(station_name: String) -> Result<String> {
let json_string = create_json_request(POINT_FINDER) let kv_pair = get_key_value_pair_for_stopid(&station_name);
.send_string(&format!(
"{{ let json_string = create_json_request(POINT_FINDER, kv_pair)
\"query\":\"{station_name}\", .context("creating JSON request")?
\"limit\":1, .into_string()
\"regionalOnly\":\"true\", .context("converting JSON reply to string")?;
\"stopsOnly\":\"true\"
}}"
))?
.into_string()?;
let response: PointFinderResponse = serde_json::from_str(&json_string)?; let response: PointFinderResponse = serde_json::from_str(&json_string)?;
print!("{}", response.point.name); print_station_name(&response);
if let Some(area) = response.point.area {
print!(", {area}");
}
println!("\n");
let result = response.point.stopid; let result = response.point.stopid;
Ok(result) 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::Response> {
ureq::post(url) ureq::post(url)
.set("Content-Type", "application/json") .set("Content-Type", "application/json")
.set("charset", "UTF-8") .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
} }
+5 -5
View File
@@ -85,15 +85,15 @@ pub struct PointFinderResponse {
pub struct Point { pub struct Point {
pub stopid: String, pub stopid: String,
pub name: String, pub name: String,
pub area: Option<String>, pub area: String,
} }
impl From<(&str, &str, Option<&str>)> for Point { impl From<(&str, &str, &str)> for Point {
fn from(value: (&str, &str, Option<&str>)) -> Self { fn from(value: (&str, &str, &str)) -> Self {
Self { Self {
stopid: value.0.into(), stopid: value.0.into(),
name: value.1.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<Point> {
let mut iter = line.trim_start_matches("\\\"").split_terminator('|'); let mut iter = line.trim_start_matches("\\\"").split_terminator('|');
let stopid = iter.next().context("getting stopid from line")?; 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")?; let name = iter.next().context("getting stop name from line")?;
Ok((stopid, name, area).into()) Ok((stopid, name, area).into())
} }