bahnreise/src/bahn_api/station_search.rs
2025-08-19 23:27:17 +02:00

97 lines
2.8 KiB
Rust

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<QueriedStations, QueryStationsError> {
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<QSStation>;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct QSStation {
#[serde(default)]
ext_id: Option<String>,
#[serde(default)]
id: Option<String>,
name: String,
#[serde(default)]
lat: Option<f64>,
#[serde(default)]
lon: Option<f64>,
#[serde(default)]
products: Vec<String>,
}
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<QueriedStation>,
}
#[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)
}
}