1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use serde::{Deserialize, Deserializer, Serialize};
use chrono::{Datelike, Local, TimeZone};
#[derive(Serialize, Deserialize, Debug)]
pub struct ForecastAPIResponse {
#[serde(alias = "location")]
at: LocationData,
current: CurrentWeatherData,
forecast: ForecastData,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LocationData {
#[serde(alias = "name")]
location: String,
#[serde(alias = "localtime_epoch", deserialize_with = "as_date_elements")]
dt: DateElements,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CurrentWeatherData {
#[serde(deserialize_with = "as_one_decimal_place")]
temp_c: String,
#[serde(deserialize_with = "as_one_decimal_place")]
temp_f: String,
#[serde(deserialize_with = "as_one_decimal_place")]
wind_mph: String,
#[serde(deserialize_with = "as_one_decimal_place")]
wind_kph: String,
#[serde(deserialize_with = "as_one_decimal_place")]
precip_mm: String,
#[serde(deserialize_with = "as_one_decimal_place")]
precip_in: String,
humidity: u8,
condition: WeatherCondition,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ForecastData {
#[serde(alias = "forecastday")]
at: Vec<WeatherReport>,
}
#[derive(Serialize, Deserialize, Debug)]
struct WeatherReport {
#[serde(alias = "date_epoch", deserialize_with = "as_date_elements")]
dt: DateElements,
day: DayWeatherData,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DayWeatherData {
#[serde(deserialize_with = "as_one_decimal_place")]
avgtemp_c: String,
#[serde(deserialize_with = "as_one_decimal_place")]
avgtemp_f: String,
condition: WeatherCondition,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DateElements {
weekday: String,
weekday_short: String,
pretty_date: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct WeatherCondition {
#[serde(alias = "text")]
description: String,
#[serde(alias = "code", deserialize_with = "as_weather_icon")]
symbol: String,
}
fn as_date_elements<'de, D>(deserializer: D) -> Result<DateElements, D::Error>
where
D: Deserializer<'de>,
{
let seconds = i64::deserialize(deserializer)?;
let local = Local.timestamp_opt(seconds, 0).unwrap();
Ok(DateElements {
weekday: local.format("%A").to_string(),
weekday_short: local.weekday().to_string(),
pretty_date: local.format("%e %B %Y").to_string(),
})
}
fn as_one_decimal_place<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let v = f32::deserialize(deserializer)?;
Ok(format!("{:.1}", v))
}
fn as_weather_icon<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let v = u16::deserialize(deserializer)?;
let icon = match v {
1003 | 1009 => "partly_cloudy_day",
1006 => "cloudy",
1087 | 1273 | 1276 | 1279 | 1282 => "thunderstorm",
1030 | 1135 | 1147 => "lens_blur", 1063 | 1150 | 1153 | 1168 | 1171 | 1180 | 1183 | 1186 | 1189 | 1192 | 1195 | 1198 | 1201 | 1240 | 1243 | 1246 => "rainy",
1066 | 1072 | 1210 | 1216 | 1219 | 1222 | 1225 | 1249 | 1252 | 1255 | 1258 | 1261 | 1264 => "weather_snowy",
1069 | 1114 | 1117 | 1204 | 1207 | 1237 => "severe_cold", _ => "sunny",
};
Ok(icon.to_string())
}