feat: caching

This commit is contained in:
Mark
2026-07-01 13:02:21 +02:00
parent 333eae6816
commit 78be279f01
2 changed files with 45 additions and 7 deletions

View File

@@ -1,10 +1,15 @@
use std::collections::{BTreeMap, HashMap};
use std::{
collections::{BTreeMap, HashMap},
time::Instant,
};
use reqwest::Url;
use serde::Deserialize;
#[derive(Default)]
pub struct DwdCache {}
pub struct DwdCache {
dwd_responses: HashMap<Vec<String>, (Instant, Result<Forecast, String>)>,
}
pub type Forecast = HashMap<String, ForecastDatas>;
#[derive(Deserialize)]
@@ -168,7 +173,40 @@ impl ForecastData {
}
}
pub async fn forecast(ids: &[&str], cache: &mut DwdCache) -> Result<Forecast, String> {
pub async fn forecast<'a>(
ids: Vec<String>,
cache: &'a mut DwdCache,
) -> Result<&'a Forecast, &'a String> {
let now = Instant::now();
let v = if let Some((time, cached)) = cache.dwd_responses.get(&ids) {
let age = now.saturating_duration_since(*time).as_secs();
if age < if cached.is_ok() { 600 } else { 90 } {
// cache is fresh, don't remove it,
// don't fetch new data.
// or_insert_with will not run.
None
} else {
// cache is outdated, remove it,
// and fetch new data.
// or_insert_with will run.
cache.dwd_responses.remove(&ids);
Some(forecast_impl(&ids).await)
}
} else {
// there is no cache,
// fetch new data.
// or_insert_with will run.
Some(forecast_impl(&ids).await)
};
cache
.dwd_responses
.entry(ids)
// closure will run exactly when `v` is `Some`
.or_insert_with(|| (now, v.unwrap()))
.1
.as_ref()
}
async fn forecast_impl(ids: &[String]) -> Result<Forecast, String> {
dbg!(ids);
let mut url =
Url::parse("https://app-prod-ws.warnwetter.de/v30/stationOverviewExtended").unwrap();

View File

@@ -196,13 +196,13 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
let q = q.to_lowercase();
dwd_stations
.range((q.clone(), 0)..=(q, usize::MAX))
.map(|(_, station)| station.station.as_str())
.map(|(_, station)| station.station.clone())
.take(WEATHER_MAX_STATIONS)
.collect()
}
LocationString::Station(q) => q
.iter()
.map(|s| s.as_str())
.map(|s| s.clone())
.take(WEATHER_MAX_STATIONS)
.collect(),
LocationString::Coords(lat, lon) => vec![
@@ -211,7 +211,7 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
.min_by(|a, b| dist2(a, *lat, *lon).total_cmp(&dist2(b, *lat, *lon)))
.unwrap()
.station
.as_str(),
.clone(),
],
};
if stations.is_empty() {
@@ -220,7 +220,7 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
"could not find any such station".to_owned(),
)));
}
match crate::dwd::forecast(&stations, dwd_cache).await {
match crate::dwd::forecast(stations, dwd_cache).await {
Ok(forecast) => {
fn forecasts(
forecast: &HashMap<String, ForecastDatas>,