210 lines
6.2 KiB
Rust
210 lines
6.2 KiB
Rust
pub mod openweathermap {
|
|
use kelikatti_api::*;
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Deserialize)]
|
|
struct MainData {
|
|
temp: Option<f32>,
|
|
feels_like: Option<f32>,
|
|
humidity: Option<f32>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct WeatherData {
|
|
main: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct WindData {
|
|
speed: Option<f32>,
|
|
deg: Option<f32>,
|
|
gust: Option<f32>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct CloudsData {
|
|
all: Option<f32>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct RainData {
|
|
#[serde(rename = "1h")]
|
|
one_hour: Option<f32>,
|
|
#[serde(rename = "3h")]
|
|
three_hour: Option<f32>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct SnowData {
|
|
#[serde(rename = "1h")]
|
|
one_hour: Option<f32>,
|
|
#[serde(rename = "3h")]
|
|
three_hour: Option<f32>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct CurrentSysData {
|
|
sunrise: Option<u64>,
|
|
sunset: Option<u64>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct WeatherResponse {
|
|
main: MainData,
|
|
weather: Vec<WeatherData>,
|
|
wind: WindData,
|
|
clouds: CloudsData,
|
|
timezone: Option<i64>,
|
|
rain: Option<RainData>,
|
|
snow: Option<SnowData>,
|
|
sys: CurrentSysData,
|
|
}
|
|
|
|
impl From<WeatherResponse> for WeatherDTO {
|
|
fn from(value: WeatherResponse) -> Self {
|
|
Self {
|
|
sunrise_utc_ms: value.sys.sunrise.map(|seconds| seconds * 1000),
|
|
sunset_utc_ms: value.sys.sunset.map(|seconds| seconds * 1000),
|
|
timezone_shift_ms: value.timezone.map(|shift| shift * 1000),
|
|
temperature: value.main.temp,
|
|
feels_like: value.main.feels_like,
|
|
wind_speed: value.wind.speed,
|
|
gust_speed: value.wind.gust,
|
|
wind_dir: value.wind.deg,
|
|
humidity: value.main.humidity.map(|percentage| percentage / 100.0),
|
|
rain_rate: value.rain.and_then(|rate| {
|
|
rate.one_hour.or(rate
|
|
.three_hour
|
|
.map(|three_hour_cumulative| three_hour_cumulative / 3.0))
|
|
}),
|
|
snow_rate: value.snow.and_then(|rate| {
|
|
rate.one_hour.or(rate
|
|
.three_hour
|
|
.map(|three_hour_cumulative| three_hour_cumulative / 3.0))
|
|
}),
|
|
cloudiness: value.clouds.all.map(|percentage| percentage / 100.0),
|
|
conditions: value
|
|
.weather
|
|
.iter()
|
|
.map(|weather| weather.main.to_lowercase())
|
|
.collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct ForecastSysData {
|
|
pod: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct ForecastDatapoint {
|
|
dt: u64,
|
|
main: MainData,
|
|
weather: Vec<WeatherData>,
|
|
wind: WindData,
|
|
clouds: CloudsData,
|
|
pop: Option<f32>,
|
|
rain: Option<RainData>,
|
|
snow: Option<SnowData>,
|
|
sys: ForecastSysData,
|
|
}
|
|
|
|
impl From<ForecastDatapoint> for ForecastDatapointDTO {
|
|
fn from(value: ForecastDatapoint) -> Self {
|
|
Self {
|
|
timestamp_utc_ms: value.dt * 1000,
|
|
is_day: value.sys.pod == "d",
|
|
temperature: value.main.temp,
|
|
feels_like: value.main.feels_like,
|
|
wind_speed: value.wind.speed,
|
|
gust_speed: value.wind.gust,
|
|
wind_dir: value.wind.deg,
|
|
humidity: value.main.humidity.map(|percentage| percentage / 100.0),
|
|
precipitation_chance: value.pop,
|
|
rain_rate: value.rain.and_then(|rate| {
|
|
rate.one_hour.or(rate
|
|
.three_hour
|
|
.map(|three_hour_cumulative| three_hour_cumulative / 3.0))
|
|
}),
|
|
snow_rate: value.snow.and_then(|rate| {
|
|
rate.one_hour.or(rate
|
|
.three_hour
|
|
.map(|three_hour_cumulative| three_hour_cumulative / 3.0))
|
|
}),
|
|
cloudiness: value.clouds.all.map(|percentage| percentage / 100.0),
|
|
conditions: value
|
|
.weather
|
|
.iter()
|
|
.map(|weather| weather.main.to_lowercase())
|
|
.collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct ForecastResponse {
|
|
list: Vec<ForecastDatapoint>,
|
|
}
|
|
|
|
impl From<ForecastResponse> for ForecastDTO {
|
|
fn from(value: ForecastResponse) -> Self {
|
|
Self {
|
|
list: value
|
|
.list
|
|
.into_iter()
|
|
.map(ForecastDatapointDTO::from)
|
|
.collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
const GEO_POS: Coordinates = Coordinates {
|
|
lat: 60.20076,
|
|
lon: 24.80352,
|
|
};
|
|
const UNIT: &str = "metric";
|
|
const API_HOST: &str = "https://api.openweathermap.org";
|
|
const API_KEY: &str = "";
|
|
|
|
fn new_client() -> reqwest::Client {
|
|
reqwest::Client::new()
|
|
}
|
|
|
|
pub async fn fetch_weather() -> reqwest::Result<WeatherDTO> {
|
|
let url = format!("{API_HOST}/data/2.5/weather");
|
|
let response = new_client()
|
|
.get(url)
|
|
.query(&[
|
|
("lat", GEO_POS.lat.to_string()),
|
|
("lon", GEO_POS.lon.to_string()),
|
|
("units", UNIT.to_string()),
|
|
("appid", API_KEY.to_string()),
|
|
])
|
|
.send()
|
|
.await?
|
|
.json::<WeatherResponse>()
|
|
.await?;
|
|
|
|
Ok(WeatherDTO::from(response))
|
|
}
|
|
|
|
pub async fn fetch_forecast() -> reqwest::Result<ForecastDTO> {
|
|
let url = format!("{API_HOST}/data/2.5/forecast");
|
|
let response = new_client()
|
|
.get(url)
|
|
.query(&[
|
|
("lat", GEO_POS.lat.to_string()),
|
|
("lon", GEO_POS.lon.to_string()),
|
|
("units", UNIT.to_string()),
|
|
("appid", API_KEY.to_string()),
|
|
])
|
|
.send()
|
|
.await?
|
|
.json::<ForecastResponse>()
|
|
.await?;
|
|
|
|
Ok(ForecastDTO::from(response))
|
|
}
|
|
}
|