use reqwest::Url; use serde::Deserialize; use crate::bahn_api::{basic_types::station_ids::StationId, transport_modes::TransportMode}; use super::{ basic_types::station_ids::{DbStopId, NamedStation, StationEvaNumber, StationIdRef}, transport_modes::TransportModesSet, }; pub async fn query_stations(query: &str) -> Result { let response = reqwest::get( Url::parse_with_params( "https://www.bahn.de/web/api/reiseloesung/orte", [("suchbegriff", query), ("typ", "ALL"), ("limit", "10")], ) .expect("hardcoded url and bahn.de-provided route id should always be valid"), ) .await .map_err(QueryStationsError::NetworkError)? .error_for_status() .map_err(QueryStationsError::NetworkError)? .text() .await .map_err(QueryStationsError::NetworkError)?; let data: QSData = serde_json::from_str(&response) .map_err(move |e| QueryStationsError::ParseError(response, e))?; type QSData = Vec; #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct QSStation { #[serde(default)] ext_id: Option, #[serde(default)] id: Option, name: String, #[serde(default)] lat: Option, #[serde(default)] lon: Option, #[serde(default)] products: Vec, } Ok(QueriedStations { stations: data .into_iter() .filter_map(|station| { Some(QueriedStation { station: NamedStation { name: station.name, id: StationId { eva_number: StationEvaNumber(station.ext_id?), db_station_id: DbStopId(station.id?), }, }, lat_lon: station .lat .and_then(|lat| station.lon.map(|lon| (lat, lon))), transport_modes: station .products .iter() .filter_map(|v| TransportMode::from_str_from_bahn_api(v)) .fold(TransportModesSet::new(), |set, v| set.with(v)), }) }) .collect(), }) } #[derive(Debug)] pub struct QueriedStations { pub stations: Vec, } #[derive(Debug)] pub struct QueriedStation { pub station: NamedStation, pub lat_lon: Option<(f64, f64)>, pub transport_modes: TransportModesSet, } #[derive(Debug)] pub enum QueryStationsError { NetworkError(reqwest::Error), ParseError(String, serde_json::Error), } impl<'a> From<&'a QueriedStation> for StationIdRef<'a> { fn from(value: &'a QueriedStation) -> Self { From::from(&value.station) } }