diff --git a/Cargo.lock b/Cargo.lock index b94a685..5b4c963 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1942,7 +1942,6 @@ dependencies = [ "color-eyre", "geocoding", "image", - "once_cell", "reqwest 0.11.9", "rocket", ] diff --git a/Cargo.toml b/Cargo.toml index ffb7b2e..e012442 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,5 @@ chrono = "0.4.19" color-eyre = "0.5.6" geocoding = "0.3.1" image = "0.24.0" -once_cell = "1.9.0" reqwest = "0.11.9" rocket = { version = "0.5.0-rc.1", features = ["json"] } diff --git a/src/main.rs b/src/main.rs index c4b3972..c8599e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,21 +11,22 @@ )] #![deny(missing_docs)] +use std::sync::Arc; + use color_eyre::Result; use geocoding::{Forward, Openstreetmap, Point}; -use once_cell::sync::Lazy; use rocket::serde::json::Json; use rocket::serde::Serialize; use rocket::tokio::sync::Mutex; use rocket::tokio::{self, select}; -use rocket::{get, routes, FromFormField}; +use rocket::{get, routes, FromFormField, State}; use self::maps::Maps; mod maps; -/// Global maps cache refreshed by a separate task. -static MAPS: Lazy> = Lazy::new(|| Mutex::new(Maps::new())); +/// A handle to access the in-memory cached maps. +type MapsHandle = Arc>; /// The current for a specific location. /// @@ -129,7 +130,12 @@ impl Metric { /// Calculates and returns the forecast. /// /// The provided list `metrics` determines what will be included in the forecast. -async fn forecast(lat: f64, lon: f64, metrics: Vec) -> Forecast { +async fn forecast( + lat: f64, + lon: f64, + metrics: Vec, + _maps_handle: &State, +) -> Forecast { let mut forecast = Forecast::new(lat, lon); // Expand the `All` metric if present, deduplicate otherwise. @@ -173,17 +179,26 @@ async fn address_position(address: String) -> Option<(f64, f64)> { /// Handler for retrieving the forecast for an address. #[get("/forecast?
&")] -async fn forecast_address(address: String, metrics: Vec) -> Option> { +async fn forecast_address( + address: String, + metrics: Vec, + maps_handle: &State, +) -> Option> { let (lat, lon) = address_position(address).await?; - let forecast = forecast(lat, lon, metrics).await; + let forecast = forecast(lat, lon, metrics, maps_handle).await; Some(Json(forecast)) } /// Handler for retrieving the forecast for a geocoded position. #[get("/forecast?&&", rank = 2)] -async fn forecast_geo(lat: f64, lon: f64, metrics: Vec) -> Json { - let forecast = forecast(lat, lon, metrics).await; +async fn forecast_geo( + lat: f64, + lon: f64, + metrics: Vec, + maps_handle: &State, +) -> Json { + let forecast = forecast(lat, lon, metrics, maps_handle).await; Json(forecast) } @@ -193,14 +208,17 @@ async fn forecast_geo(lat: f64, lon: f64, metrics: Vec) -> Json Result<()> { color_eyre::install()?; + let maps = Maps::new(); + let maps_handle = Arc::new(Mutex::new(maps)); + let maps_updater = tokio::spawn(maps::run(Arc::clone(&maps_handle))); + let rocket = rocket::build() + .manage(maps_handle) .mount("/", routes![forecast_address, forecast_geo]) .ignite() .await?; let shutdown = rocket.shutdown(); - let maps_updater = tokio::spawn(maps::run()); - select! { result = rocket.launch() => { result? diff --git a/src/maps.rs b/src/maps.rs index e53ba46..e75254f 100644 --- a/src/maps.rs +++ b/src/maps.rs @@ -7,7 +7,7 @@ use chrono::DurationRound; use image::DynamicImage; use rocket::tokio::time::{sleep, Duration, Instant}; -use crate::MAPS; +use crate::MapsHandle; /// The interval between map refreshes (in seconds). const SLEEP_INTERVAL: Duration = Duration::from_secs(60); @@ -37,14 +37,14 @@ const UVI_BASE_URL: &str = "https://image.buienradar.nl/2.0/image/sprite/Weather const UVI_INTERVAL: Duration = Duration::from_secs(3600); /// Runs a loop that keeps refreshing the maps when necessary. -pub(crate) async fn run() -> ! { +pub(crate) async fn run(maps_handle: MapsHandle) -> ! { loop { - let mut maps = MAPS.lock().await; - println!("🕔 Refreshing the maps (if necessary)..."); - maps.refresh_precipitation().await; - maps.refresh_pollen().await; - maps.refresh_uvi().await; + + // FIXME: Refactor this so that the lock is only held when updating the maps fields. + maps_handle.lock().await.refresh_precipitation().await; + maps_handle.lock().await.refresh_pollen().await; + maps_handle.lock().await.refresh_uvi().await; sleep(SLEEP_INTERVAL).await; }