add point finder from args

This commit is contained in:
Dr. Matthias Ratajczak
2023-01-05 13:31:51 +01:00
parent 508684fd72
commit 65881f7174
5 changed files with 107 additions and 10 deletions
+10
View File
@@ -0,0 +1,10 @@
use std::env::args;
use anyhow::{Context, Result};
pub fn get_station_name_from_args() -> Result<String> {
args()
.into_iter()
.nth(1)
.context("getting station name from args")
}
+3 -1
View File
@@ -1,5 +1,7 @@
mod args;
mod query;
pub mod response;
pub use query::query_server;
pub use args::get_station_name_from_args;
pub use query::get_departures_for_station;
pub use response::format_departures;
+3 -2
View File
@@ -1,9 +1,10 @@
use anyhow::Result;
use departure::{format_departures, query_server};
use departure::{format_departures, get_departures_for_station, get_station_name_from_args};
fn main() -> Result<()> {
let response = query_server()?;
let station_name = get_station_name_from_args()?;
let response = get_departures_for_station(station_name)?;
let formatted = format_departures(response);
println!("{formatted}");
Ok(())
+42 -6
View File
@@ -1,16 +1,52 @@
use anyhow::Result;
use ureq::Request;
use crate::response::Response;
use crate::response::{PointFinderResponse, Response};
const DEPARTURE_MONITOR: &str = "https://webapi.vvo-online.de/dm";
const POINT_FINDER: &str = "https://webapi.vvo-online.de/tr/pointfinder";
pub fn query_server() -> Result<Response> {
let json_string = ureq::post(DEPARTURE_MONITOR)
.set("Content-Type", "application/json")
.set("charset", "UTF-8")
.send_string("{\"stopid\":\"33006765\",\"limit\":5}")?
pub fn get_departures_for_station(station_name: String) -> Result<Response> {
let stopid = get_stopid_from_station_name(station_name)?;
let json_string = create_json_request(DEPARTURE_MONITOR)
.send_string(&format!(
"{{
\"stopid\":\"{stopid}\",
\"limit\":5
}}"
))?
.into_string()?;
let result = serde_json::from_str(&json_string)?;
Ok(result)
}
fn get_stopid_from_station_name(station_name: String) -> Result<String> {
let json_string = create_json_request(POINT_FINDER)
.send_string(&format!(
"{{
\"query\":\"{station_name}\",
\"limit\":1,
\"regionalOnly\":\"true\",
\"stopsOnly\":\"true\"
}}"
))?
.into_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");
let result = response.point.stopid;
Ok(result)
}
fn create_json_request(url: &str) -> Request {
ureq::post(url)
.set("Content-Type", "application/json")
.set("charset", "UTF-8")
}
+49 -1
View File
@@ -1,6 +1,6 @@
use std::fmt::{Debug, Display};
use anyhow::Result;
use anyhow::{Context, Result};
use chrono::{DateTime, Local, TimeZone, Utc};
use serde::{de, Deserialize, Deserializer};
@@ -72,3 +72,51 @@ pub fn format_departures(response: Response) -> String {
let departures: Vec<String> = response.departures.iter().map(|d| d.to_string()).collect();
departures.join("\n\n")
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct PointFinderResponse {
#[serde(deserialize_with = "deserialize_points", rename = "Points")]
pub point: Point,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct Point {
pub stopid: String,
pub name: String,
pub area: Option<String>,
}
impl From<(&str, &str, Option<&str>)> for Point {
fn from(value: (&str, &str, Option<&str>)) -> Self {
Self {
stopid: value.0.into(),
name: value.1.into(),
area: value.2.map(|s| s.into()),
}
}
}
/// Deserialize a Point Response in the form of
/// '\"33000742|||Helmholtzstraße|5655904|4621157|0||\"'
fn extract_data_from_response(line: &str) -> Result<Point> {
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 name = iter.next().context("getting stop name from line")?;
Ok((stopid, name, area).into())
}
fn deserialize_points<'de, D>(deserializer: D) -> Result<Point, D::Error>
where
D: Deserializer<'de>,
{
let stops: Vec<String> = Vec::deserialize(deserializer)?;
let first_stop_line = stops
.first()
.ok_or_else(|| de::Error::custom("could not get first stop"))?;
let point = extract_data_from_response(first_stop_line).map_err(de::Error::custom)?;
Ok(point)
}