use anyhow::Result; use serde::Serialize; use shared::chart::{render, Format}; use shared::emoji; use shared::units::Units; use shared::weather::Weather; const DATE_OUTPUT_FORMAT: &str = "%b %-d, %H:%M"; #[derive(Serialize, Debug)] pub struct Notification { pub title: String, pub subtitle: String, pub body: String, } pub trait WeatherExt { fn as_notification(&self) -> Result; fn as_string(&self, json: bool, chart: bool, width: Option) -> Result; } impl<'a> WeatherExt for Weather<'a> { fn as_notification(&self) -> Result { let title = match &self.name { Some(name) => format!( "{} {} ({})", emoji::CALENDAR, name, self.date.format(DATE_OUTPUT_FORMAT), ), None => "".to_string(), }; let subtitle = format!("{} {}", emoji::GLOBE, self.location); let temperature = match self.units { Units::Metric => "C", Units::Imperial => "F", }; let speed = match self.units { Units::Metric => "km/h", Units::Imperial => "mph", }; let body = format!( "{} {}°{} (feels like {}°{})\n{} {}% chance of rain & {}% humidity\n{} {}{} {}", emoji::emoji_for_weather(self.weather_code)?, self.temperature.round(), temperature, self.feels_like.round(), temperature, emoji::PRECIPITATION, self.probability_of_precipitation, self.humidity, emoji::WIND, self.wind_speed.round(), speed, shared::weather::wind_direction(self.wind_direction)?, ); Ok(Notification { title, subtitle, body, }) } fn as_string(&self, json: bool, chart: bool, width: Option) -> Result { if json { return Ok(serde_json::to_string(&self)?); } let notification = self.as_notification()?; let mut text = if chart { format!("{}\n", notification.subtitle) } else { format!( "{} {}\n{}", notification.title, notification.subtitle, notification.body.replace('\n', " "), ) }; if chart && !self.forecast.is_empty() { let (chart, _) = render( self, Format::Ascii { width_override: width, }, )?; text.push_str(&chart); } Ok(text) } }