1use alloc::str::FromStr;
2use alloc::string::String;
3use alloc::vec::Vec;
4use serde::Deserialize;
5
6use crate::units::Coordinates;
7use crate::{Daily, Error, Hourly, ThreeHourly, TimePeriod};
8
9pub(crate) trait RawTimePeriod: Sized {
10 type Output: TimePeriod + TryFrom<Self, Error = Error>;
11}
12
13impl RawTimePeriod for RawHourlyForecast {
14 type Output = Hourly;
15}
16
17impl RawTimePeriod for RawThreeHourlyForecast {
18 type Output = ThreeHourly;
19}
20
21impl RawTimePeriod for RawDailyForecast {
22 type Output = Daily;
23}
24
25#[derive(Debug, Deserialize)]
26pub(crate) struct RawForecast<T>
27where
28 T: RawTimePeriod,
29{
30 pub features: Vec<RawFeature<T>>,
31}
32
33#[derive(Debug, Deserialize)]
34pub(crate) struct RawFeature<T> {
35 pub geometry: Geometry,
36 pub properties: Properties<T>,
37}
38
39#[derive(Debug, Deserialize)]
40pub(crate) struct Geometry {
41 pub coordinates: Coordinates,
42}
43
44#[derive(Debug, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub(crate) struct Properties<T> {
47 pub location: Location,
48 pub request_point_distance: f32,
49 #[serde(deserialize_with = "utc_minutes")]
50 pub model_run_date: jiff::Zoned,
51 pub time_series: Vec<T>,
52}
53
54#[derive(Debug, Deserialize)]
55pub(crate) struct Location {
56 pub name: String,
57}
58
59fn utc_minutes<'de, D>(d: D) -> Result<jiff::Zoned, D::Error>
60where
61 D: serde::Deserializer<'de>,
62{
63 let s = String::deserialize(d)?;
64 jiff::Timestamp::from_str(&s)
65 .map(|ts| ts.to_zoned(jiff::tz::TimeZone::UTC))
66 .map_err(|_| serde::de::Error::custom("Failed to parse datetime."))
67}
68
69#[derive(Debug, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub(crate) struct RawHourlyForecast {
72 #[serde(deserialize_with = "utc_minutes")]
74 pub time: jiff::Zoned,
75 pub screen_temperature: f32,
79 pub max_screen_air_temp: Option<f32>,
83 pub min_screen_air_temp: Option<f32>,
87 pub screen_dew_point_temperature: f32,
91 pub feels_like_temperature: f32,
94 pub wind_speed_10m: f32,
99 pub wind_direction_from_10m: f32,
105 pub wind_gust_speed_10m: f32,
107 pub max_10m_wind_gust: Option<f32>,
111 pub visibility: f32,
113 pub screen_relative_humidity: f32,
115 pub mslp: u32,
117 pub uv_index: u8,
120 pub significant_weather_code: i8,
125 pub precipitation_rate: f32,
127 pub total_precip_amount: Option<f32>,
132 pub total_snow_amount: Option<f32>,
141 pub prob_of_precipitation: f32,
143}
144
145#[derive(Debug, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub(crate) struct RawThreeHourlyForecast {
148 #[serde(deserialize_with = "utc_minutes")]
150 pub time: jiff::Zoned,
151 pub max_screen_air_temp: f32,
153 pub min_screen_air_temp: f32,
155 pub feels_like_temp: f32,
158 pub wind_speed_10m: f32,
160 pub wind_direction_from_10m: f32,
162 pub wind_gust_speed_10m: f32,
164 pub max_10m_wind_gust: f32,
166 pub visibility: f32,
168 pub screen_relative_humidity: f32,
170 pub mslp: u32,
172 pub uv_index: u8,
175 pub significant_weather_code: i8,
180 pub total_precip_amount: f32,
183 pub total_snow_amount: f32,
190 pub prob_of_precipitation: f32,
192 pub prob_of_snow: f32,
193 pub prob_of_heavy_snow: f32,
194 pub prob_of_rain: f32,
195 pub prob_of_heavy_rain: f32,
196 pub prob_of_hail: f32,
197 pub prob_of_sferics: f32,
198}
199
200#[derive(Debug, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub(crate) struct RawDailyForecast {
203 #[serde(deserialize_with = "utc_minutes")]
205 pub time: jiff::Zoned,
206 pub day_significant_weather_code: Option<i8>,
207 pub day_max_screen_temperature: f32,
208 pub day_upper_bound_max_temp: f32,
209 pub day_lower_bound_max_temp: f32,
210 pub day_max_feels_like_temp: Option<f32>,
211 pub day_upper_bound_max_feels_like_temp: f32,
212 pub day_lower_bound_max_feels_like_temp: f32,
213 pub day_probability_of_precipitation: Option<f32>,
214 pub day_probability_of_rain: Option<f32>,
215 pub day_probability_of_heavy_rain: Option<f32>,
216 pub day_probability_of_snow: Option<f32>,
217 pub day_probability_of_heavy_snow: Option<f32>,
218 pub day_probability_of_hail: Option<f32>,
219 pub day_probability_of_sferics: Option<f32>,
220 pub max_uv_index: Option<u8>,
221 #[serde(rename = "midday10MWindSpeed")]
222 pub midday_10m_wind_speed: f32,
223 #[serde(rename = "midday10MWindDirection")]
224 pub midday_10m_wind_direction: f32,
225 #[serde(rename = "midday10MWindGust")]
226 pub midday_10m_wind_gust: f32,
227 pub midday_mslp: u32,
228 pub midday_relative_humidity: f32,
229 pub midday_visibility: f32,
230 pub night_significant_weather_code: i8,
231 pub night_min_screen_temperature: f32,
232 pub night_upper_bound_min_temp: f32,
233 pub night_lower_bound_min_temp: f32,
234 pub night_min_feels_like_temp: f32,
235 pub night_upper_bound_min_feels_like_temp: f32,
236 pub night_lower_bound_min_feels_like_temp: f32,
237 pub night_probability_of_precipitation: f32,
238 pub night_probability_of_rain: f32,
239 pub night_probability_of_heavy_rain: f32,
240 pub night_probability_of_snow: f32,
241 pub night_probability_of_heavy_snow: f32,
242 pub night_probability_of_hail: f32,
243 pub night_probability_of_sferics: f32,
244 #[serde(rename = "midnight10MWindSpeed")]
245 pub midnight_10m_wind_speed: f32,
246 #[serde(rename = "midnight10MWindDirection")]
247 pub midnight_10m_wind_direction: f32,
248 #[serde(rename = "midnight10MWindGust")]
249 pub midnight_10m_wind_gust: f32,
250 pub midnight_mslp: u32,
251 pub midnight_relative_humidity: f32,
252 pub midnight_visibility: f32,
253}