1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
5#[repr(u8)]
6#[non_exhaustive]
7pub enum WmoCode {
8 ClearSky = 0,
10 MainlyClear = 1,
12 PartlyCloudy = 2,
14 Overcast = 3,
16 Fog = 45,
18 DepositingRimeFog = 48,
20 DrizzleLight = 51,
22 DrizzleModerate = 53,
24 DrizzleDense = 55,
26 FreezingDrizzleLight = 56,
28 FreezingDrizzleDense = 57,
30 RainSlight = 61,
32 RainModerate = 63,
34 RainHeavy = 65,
36 FreezingRainLight = 66,
38 FreezingRainHeavy = 67,
40 SnowFallSlight = 71,
42 SnowFallModerate = 73,
44 SnowFallHeavy = 75,
46 SnowGrains = 77,
48 RainShowersSlight = 80,
50 RainShowersModerate = 81,
52 RainShowersViolent = 82,
54 SnowShowersSlight = 85,
56 SnowShowersHeavy = 86,
58 ThunderstormSlightOrModerate = 95,
60 ThunderstormWithSlightHail = 96,
62 ThunderstormWithHeavyHail = 99,
64}
65
66impl WmoCode {
67 pub fn from_code(code: u8) -> Option<Self> {
69 Some(match code {
70 0 => Self::ClearSky,
71 1 => Self::MainlyClear,
72 2 => Self::PartlyCloudy,
73 3 => Self::Overcast,
74 45 => Self::Fog,
75 48 => Self::DepositingRimeFog,
76 51 => Self::DrizzleLight,
77 53 => Self::DrizzleModerate,
78 55 => Self::DrizzleDense,
79 56 => Self::FreezingDrizzleLight,
80 57 => Self::FreezingDrizzleDense,
81 61 => Self::RainSlight,
82 63 => Self::RainModerate,
83 65 => Self::RainHeavy,
84 66 => Self::FreezingRainLight,
85 67 => Self::FreezingRainHeavy,
86 71 => Self::SnowFallSlight,
87 73 => Self::SnowFallModerate,
88 75 => Self::SnowFallHeavy,
89 77 => Self::SnowGrains,
90 80 => Self::RainShowersSlight,
91 81 => Self::RainShowersModerate,
92 82 => Self::RainShowersViolent,
93 85 => Self::SnowShowersSlight,
94 86 => Self::SnowShowersHeavy,
95 95 => Self::ThunderstormSlightOrModerate,
96 96 => Self::ThunderstormWithSlightHail,
97 99 => Self::ThunderstormWithHeavyHail,
98 _ => return None,
99 })
100 }
101
102 pub fn description(self) -> &'static str {
104 match self {
105 Self::ClearSky => "clear sky",
106 Self::MainlyClear => "mainly clear",
107 Self::PartlyCloudy => "partly cloudy",
108 Self::Overcast => "overcast",
109 Self::Fog => "fog",
110 Self::DepositingRimeFog => "depositing rime fog",
111 Self::DrizzleLight => "light drizzle",
112 Self::DrizzleModerate => "moderate drizzle",
113 Self::DrizzleDense => "dense drizzle",
114 Self::FreezingDrizzleLight => "light freezing drizzle",
115 Self::FreezingDrizzleDense => "dense freezing drizzle",
116 Self::RainSlight => "slight rain",
117 Self::RainModerate => "moderate rain",
118 Self::RainHeavy => "heavy rain",
119 Self::FreezingRainLight => "light freezing rain",
120 Self::FreezingRainHeavy => "heavy freezing rain",
121 Self::SnowFallSlight => "slight snow fall",
122 Self::SnowFallModerate => "moderate snow fall",
123 Self::SnowFallHeavy => "heavy snow fall",
124 Self::SnowGrains => "snow grains",
125 Self::RainShowersSlight => "slight rain showers",
126 Self::RainShowersModerate => "moderate rain showers",
127 Self::RainShowersViolent => "violent rain showers",
128 Self::SnowShowersSlight => "slight snow showers",
129 Self::SnowShowersHeavy => "heavy snow showers",
130 Self::ThunderstormSlightOrModerate => "slight or moderate thunderstorm",
131 Self::ThunderstormWithSlightHail => "thunderstorm with slight hail",
132 Self::ThunderstormWithHeavyHail => "thunderstorm with heavy hail",
133 }
134 }
135
136 pub fn is_precipitating(self) -> bool {
138 matches!(
139 self,
140 Self::DrizzleLight
141 | Self::DrizzleModerate
142 | Self::DrizzleDense
143 | Self::FreezingDrizzleLight
144 | Self::FreezingDrizzleDense
145 | Self::RainSlight
146 | Self::RainModerate
147 | Self::RainHeavy
148 | Self::FreezingRainLight
149 | Self::FreezingRainHeavy
150 | Self::SnowFallSlight
151 | Self::SnowFallModerate
152 | Self::SnowFallHeavy
153 | Self::SnowGrains
154 | Self::RainShowersSlight
155 | Self::RainShowersModerate
156 | Self::RainShowersViolent
157 | Self::SnowShowersSlight
158 | Self::SnowShowersHeavy
159 | Self::ThunderstormSlightOrModerate
160 | Self::ThunderstormWithSlightHail
161 | Self::ThunderstormWithHeavyHail
162 )
163 }
164
165 pub fn is_thunderstorm(self) -> bool {
167 matches!(
168 self,
169 Self::ThunderstormSlightOrModerate
170 | Self::ThunderstormWithSlightHail
171 | Self::ThunderstormWithHeavyHail
172 )
173 }
174
175 pub fn severity(self) -> Severity {
177 match self {
178 Self::ClearSky | Self::MainlyClear | Self::PartlyCloudy => Severity::Clear,
179 Self::DrizzleLight
180 | Self::FreezingDrizzleLight
181 | Self::RainSlight
182 | Self::FreezingRainLight
183 | Self::SnowFallSlight
184 | Self::RainShowersSlight
185 | Self::SnowShowersSlight => Severity::Light,
186 Self::Overcast
187 | Self::Fog
188 | Self::DepositingRimeFog
189 | Self::DrizzleModerate
190 | Self::RainModerate
191 | Self::SnowFallModerate
192 | Self::SnowGrains
193 | Self::RainShowersModerate
194 | Self::ThunderstormSlightOrModerate => Severity::Moderate,
195 Self::DrizzleDense
196 | Self::FreezingDrizzleDense
197 | Self::RainHeavy
198 | Self::FreezingRainHeavy
199 | Self::SnowFallHeavy
200 | Self::RainShowersViolent
201 | Self::SnowShowersHeavy => Severity::Heavy,
202 Self::ThunderstormWithSlightHail | Self::ThunderstormWithHeavyHail => Severity::Severe,
203 }
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::{Severity, WmoCode};
210
211 #[test]
212 fn all_documented_open_meteo_codes_are_supported() {
213 for (code, expected) in [
214 (0, WmoCode::ClearSky),
215 (1, WmoCode::MainlyClear),
216 (2, WmoCode::PartlyCloudy),
217 (3, WmoCode::Overcast),
218 (45, WmoCode::Fog),
219 (48, WmoCode::DepositingRimeFog),
220 (51, WmoCode::DrizzleLight),
221 (53, WmoCode::DrizzleModerate),
222 (55, WmoCode::DrizzleDense),
223 (56, WmoCode::FreezingDrizzleLight),
224 (57, WmoCode::FreezingDrizzleDense),
225 (61, WmoCode::RainSlight),
226 (63, WmoCode::RainModerate),
227 (65, WmoCode::RainHeavy),
228 (66, WmoCode::FreezingRainLight),
229 (67, WmoCode::FreezingRainHeavy),
230 (71, WmoCode::SnowFallSlight),
231 (73, WmoCode::SnowFallModerate),
232 (75, WmoCode::SnowFallHeavy),
233 (77, WmoCode::SnowGrains),
234 (80, WmoCode::RainShowersSlight),
235 (81, WmoCode::RainShowersModerate),
236 (82, WmoCode::RainShowersViolent),
237 (85, WmoCode::SnowShowersSlight),
238 (86, WmoCode::SnowShowersHeavy),
239 (95, WmoCode::ThunderstormSlightOrModerate),
240 (96, WmoCode::ThunderstormWithSlightHail),
241 (99, WmoCode::ThunderstormWithHeavyHail),
242 ] {
243 assert_eq!(WmoCode::from_code(code), Some(expected));
244 assert!(!expected.description().is_empty());
245 }
246
247 assert_eq!(WmoCode::from_code(4), None);
248 }
249
250 #[test]
251 fn weather_code_helpers_classify_precipitation_and_storms() {
252 assert!(!WmoCode::ClearSky.is_precipitating());
253 assert!(WmoCode::FreezingRainLight.is_precipitating());
254 assert!(WmoCode::RainShowersViolent.is_precipitating());
255 assert!(WmoCode::ThunderstormWithSlightHail.is_thunderstorm());
256 assert_eq!(
257 WmoCode::ThunderstormWithHeavyHail.severity(),
258 Severity::Severe
259 );
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
265#[non_exhaustive]
266pub enum Severity {
267 Clear,
269 Light,
271 Moderate,
273 Heavy,
275 Severe,
277}