use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Coord { pub lon: f64, pub lat: f64, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Weather { pub id: u32, pub main: String, pub description: String, pub icon: String, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Main { pub temp: Option, #[serde(default)] pub feels_like: Option, #[serde(default)] pub temp_min: Option, #[serde(default)] pub temp_max: Option, #[serde(default)] pub pressure: Option, #[serde(default)] pub humidity: Option, #[serde(default)] pub sea_level: Option, #[serde(default)] pub grnd_level: Option, } #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct Wind { pub speed: Option, pub deg: Option, pub gust: Option, } #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct Clouds { pub all: Option, } #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct Precipitation { #[serde(rename = "1h", default)] pub one_hour: Option, #[serde(rename = "3h", default)] pub three_hour: Option, } #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct Sys { pub r#type: Option, pub id: Option, pub country: Option, #[serde(with = "chrono::serde::ts_seconds_option")] pub sunrise: Option>, #[serde(with = "chrono::serde::ts_seconds_option")] pub sunset: Option>, pub message: Option, } #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct WeatherResponse { #[serde(default)] pub coord: Option, #[serde(default)] pub weather: Vec, #[serde(default)] pub base: Option, #[serde(default)] pub main: Option
, #[serde(default)] pub visibility: Option, #[serde(with = "chrono::serde::ts_seconds_option")] pub dt: Option>, #[serde(default)] pub sys: Option, #[serde(default)] pub timezone: Option, #[serde(default)] pub id: Option, #[serde(default)] pub name: Option, #[serde(default)] pub wind: Option, #[serde(default)] pub clouds: Option, #[serde(default)] pub rain: Option, #[serde(default)] pub snow: Option, #[serde(default)] pub cod: Option, #[serde(default)] pub message: Option, } impl WeatherResponse { pub fn primary_weather(&self) -> Option<&Weather> { self.weather.first() } pub fn is_success(&self) -> bool { matches!(self.cod, Some(200)) } pub fn temperature(&self) -> Option { self.main.as_ref().map(|m| m.temp)? } pub fn city(&self) -> Option<&str> { self.name.as_deref() } pub fn country(&self) -> Option<&str> { self.sys.as_ref()?.country.as_deref() } pub fn summary(&self) -> String { let temp = self .temperature() .map_or("-".into(), |t| format!("{t:.1}°")); let desc = self .primary_weather() .map_or("N/A", |w| w.description.as_str()); format!("{desc}, {temp}") } }