152 lines
3.5 KiB
Rust
152 lines
3.5 KiB
Rust
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<Utc>,
|
|
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<String> {
|
|
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)
|
|
}
|