From d432bb4cd620363b09eafa3b534d52eff3cc7efe Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Sun, 13 Feb 2022 13:10:12 +0100 Subject: [PATCH] Use URL objects instead of formatted strings Use `request::Url` for this, so we don't have to depend on the `url` crate ourselves. Also, make the URL constants more uniform. --- src/maps.rs | 36 ++++++++++++++++++++--------------- src/providers/luchtmeetnet.rs | 12 +++++++----- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/maps.rs b/src/maps.rs index 038dbe9..68e17a0 100644 --- a/src/maps.rs +++ b/src/maps.rs @@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex}; use chrono::DurationRound; use image::DynamicImage; +use reqwest::Url; use rocket::tokio::time::{sleep, Duration, Instant}; /// A handle to access the in-memory cached maps. @@ -18,8 +19,8 @@ const REFRESH_INTERVAL: Duration = Duration::from_secs(60); /// The base URL for retrieving the pollen maps from Buienradar. const POLLEN_BASE_URL: &str = "https://image.buienradar.nl/2.0/image/sprite/WeatherMapPollenRadarHourlyNL\ - ?height=988&width=820&extension=png&renderBackground=False&renderBranding=False\ - &renderText=False&history=0&forecast=24&skip=0×tamp="; + ?width=820&height=988&extension=png&renderBackground=False&renderBranding=False\ + &renderText=False&history=0&forecast=24&skip=0"; /// The interval for retrieving pollen maps. const POLLEN_INTERVAL: Duration = Duration::from_secs(600); @@ -33,8 +34,8 @@ const PRECIPITATION_INTERVAL: Duration = Duration::from_secs(300); /// The base URL for retrieving the UV index maps from Buienradar. const UVI_BASE_URL: &str = "https://image.buienradar.nl/2.0/image/sprite/WeatherMapUVIndexNL\ - ?extension=png&width=820&height=988&renderText=False&renderBranding=False\ - &renderBackground=False&history=0&forecast=5&skip=0×tamp="; + ?width=820&height=988extension=png&&renderBackground=False&renderBranding=False\ + &renderText=False&history=0&forecast=5&skip=0"; /// The interval for retrieving UV index maps. const UVI_INTERVAL: Duration = Duration::from_secs(3600); @@ -144,7 +145,7 @@ impl MapsRefresh for MapsHandle { /// /// This returns [`None`] if it fails in either performing the request, retrieving the bytes from /// the image or loading and the decoding the data into [`DynamicImage`]. -async fn retrieve_image(url: &str) -> Option { +async fn retrieve_image(url: Url) -> Option { // TODO: Handle or log errors! let response = reqwest::get(url).await.ok()?; let bytes = response.bytes().await.ok()?; @@ -156,11 +157,12 @@ async fn retrieve_image(url: &str) -> Option { /// /// See [`POLLEN_BASE_URL`] for the base URL and [`retrieve_image`] for the retrieval function. async fn retrieve_pollen_maps() -> Option { - let timestamp = chrono::Local::now().format("%y%m%d%H%M"); - let url = format!("{POLLEN_BASE_URL}{timestamp}"); + let timestamp = format!("{}", chrono::Local::now().format("%y%m%d%H%M")); + let mut url = Url::parse(POLLEN_BASE_URL).unwrap(); + url.query_pairs_mut().append_pair("timestamp", ×tamp); println!("🔽 Refreshing pollen maps from: {}", url); - retrieve_image(&url).await + retrieve_image(url).await } /// Retrieves the pollen maps from Weerplaza. @@ -172,14 +174,17 @@ async fn retrieve_precipitation_maps() -> [Option; 24] { // This only fails if timestamps and durations exceed limits! .duration_trunc(chrono::Duration::minutes(5)) .unwrap(); - let timestamp = just_before.format("%Y%m%d%H%M"); + let timestamp_prefix = just_before.format("%Y%m%d%H%M"); + let base_url = Url::parse(PRECIPITATION_BASE_URL).unwrap(); let mut precipitation: [Option; 24] = Default::default(); for (index, map) in precipitation.iter_mut().enumerate() { - let suffix = format!("{:03}", index * 5); - let url = format!("{PRECIPITATION_BASE_URL}/{timestamp}_{suffix}"); + let timestamp = format!("{timestamp_prefix}_{:03}", index * 5); + let mut url = base_url.clone(); + url.path_segments_mut().unwrap().push(×tamp); + println!("🔽 Refreshing precipitation map from: {}", url); - *map = retrieve_image(&url).await; + *map = retrieve_image(url).await; } precipitation @@ -189,11 +194,12 @@ async fn retrieve_precipitation_maps() -> [Option; 24] { /// /// See [`UVI_BASE_URL`] for the base URL and [`retrieve_image`] for the retrieval function. async fn retrieve_uvi_maps() -> Option { - let timestamp = chrono::Local::now().format("%y%m%d%H%M"); - let url = format!("{UVI_BASE_URL}{timestamp}"); + let timestamp = format!("{}", chrono::Local::now().format("%y%m%d%H%M")); + let mut url = Url::parse(UVI_BASE_URL).unwrap(); + url.query_pairs_mut().append_pair("timestamp", ×tamp); println!("🔽 Refreshing UV index maps from: {}", url); - retrieve_image(&url).await + retrieve_image(url).await } /// Runs a loop that keeps refreshing the maps when necessary. diff --git a/src/providers/luchtmeetnet.rs b/src/providers/luchtmeetnet.rs index 018c1cc..a704e31 100644 --- a/src/providers/luchtmeetnet.rs +++ b/src/providers/luchtmeetnet.rs @@ -4,6 +4,7 @@ use chrono::serde::ts_seconds; use chrono::{DateTime, Utc}; +use reqwest::Url; use rocket::serde::{Deserialize, Serialize}; use crate::Metric; @@ -48,13 +49,14 @@ pub(crate) async fn get(lat: f64, lon: f64, metric: Metric) -> Option> Metric::PM10 => "pm10", _ => return None, // Unsupported metric }; - let url = format!( - "{LUCHTMEETNET_BASE_URL}?formula={formula}&latitude={:.05}&longitude={:.05}", - lat, lon - ); + let mut url = Url::parse(LUCHTMEETNET_BASE_URL).unwrap(); + url.query_pairs_mut() + .append_pair("formula", formula) + .append_pair("latitude", &format!("{:.05}", lat)) + .append_pair("longitude", &format!("{:.05}", lon)); println!("▶️ Retrieving Luchtmeetnet data from {url}"); - let response = reqwest::get(&url).await.ok()?; + let response = reqwest::get(url).await.ok()?; let root: Container = match response.error_for_status() { Ok(res) => res.json().await.ok()?, Err(_err) => return None,