use crate::collector::Collector; use chrono::{serde::ts_seconds, DateTime, Utc}; use reqwest::get; use serde::Deserialize; use std::fmt::{self, Display, Formatter}; #[derive(Deserialize)] struct Response { current: CurrentBody, } #[derive(Deserialize)] #[allow(dead_code)] struct Condition { text: String, icon: String, code: i64, } #[derive(Deserialize)] #[allow(dead_code)] struct CurrentBody { #[serde(with = "ts_seconds")] last_updated_epoch: DateTime, last_updated: String, temp_c: f64, is_day: i8, condition: Condition, wind_kph: f64, wind_degree: f64, wind_dir: String, pressure_mb: f64, precip_mm: f64, humidity: f64, cloud: f64, feelslike_c: f64, vis_km: f64, uv: f64, gust_kph: f64, } pub struct WeatherApi { pub api_key: String, pub location: String, } #[async_trait::async_trait] impl Collector for WeatherApi { async fn collect(&mut self) -> anyhow::Result { let path = "https://api.weatherapi.com/v1/current.json"; let api_key = &self.api_key; let location = &self.location; let url = format!("{path}?key={api_key}&q={location}&aqi=no"); let Response { current: CurrentBody { last_updated_epoch, temp_c, feelslike_c, wind_kph, precip_mm, humidity, cloud, .. }, .. } = get(&url).await?.json().await?; let wind_mps = (wind_kph / 3.6).round(); let wind = wind_speed_to_beaufort(wind_mps); let temp = if feelslike_c != temp_c { format!("**{temp_c}°** (känns som {feelslike_c}°)") } else { format!("**{temp_c}°**") }; let time_of_day = last_updated_epoch.naive_local().format("%H:%M"); let markdown = format!( r#" # Väder ## {location} kl {time_of_day} {temp}. **{wind}** ({wind_mps} m/s). {cloud}% molntäcke, {precip_mm} mm regn, {humidity}% luftfuktighet. "# ); let html = markdown::to_html(markdown.trim()); Ok(html) } } /// Wind speed expressed on the beaufort scale struct BeaufortScale(u8); impl Display for BeaufortScale { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let name = match self { BeaufortScale(0) => "Lugnt", BeaufortScale(1 | 2) => "Svag vind", BeaufortScale(3 | 4) => "Måttlig vind", BeaufortScale(5 | 6) => "Frisk vind", BeaufortScale(7 | 8) => "Hård vind", BeaufortScale(9) => "Mycket hård vind", BeaufortScale(10) => "Storm", BeaufortScale(11) => "Svår storm", BeaufortScale(12..) => "Orkan", }; write!(f, "{name}") } } fn wind_speed_to_beaufort(mps: f64) -> BeaufortScale { let beaufort_wind_speeds = [ 0.0..0.3, 0.3..1.6, 1.6..3.4, 3.4..5.5, 5.5..8.8, 8.0..10.8, 10.8..13.9, 13.9..17.2, 17.2..20.8, 20.8..24.5, 24.5..28.5, 28.5..32.7, 32.7..37.0, 37.0..41.5, 41.5..46.2, 46.2..51.0, 51.0..56.1, 56.1..61.3, ]; let mps = mps.max(0.0); let index = beaufort_wind_speeds .iter() .enumerate() .find_map(|(i, range)| range.contains(&mps).then(|| i)) .unwrap_or(beaufort_wind_speeds.len()); BeaufortScale(index as u8) }