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 { pub cod: Option, // Http code pub message: Option, // Http error message pub coord: Option, #[serde(default)] pub weather: Vec, pub base: Option, pub main: Option
, pub visibility: Option, #[serde(with = "chrono::serde::ts_seconds_option")] pub dt: Option>, pub sys: Option, pub timezone: Option, pub id: Option, pub name: Option, #[serde(default)] pub wind: Option, #[serde(default)] pub clouds: Option, #[serde(default)] pub rain: Option, // "rain": { "1h": f32 } or "3h": f32 #[serde(default)] pub snow: Option, // "snow": { "1h": f32 } or "3h": f32 } 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}") } }