Something went wrong. Try again.
🌤 Track the weather for event days wed.fyi
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107use crate::event::Event;use crate::weather::Weather;use anyhow::Result;use base64::Engine;
pub fn to_png(data_uri: &str) -> Result<Vec<u8>> { let b64 = data_uri .strip_prefix("data:image/png;base64,") .ok_or_else(|| anyhow::anyhow!("unexpected PNG data URI format"))?; base64::engine::general_purpose::STANDARD .decode(b64) .map_err(|e| anyhow::anyhow!("base64 decode failed: {e}"))}
pub fn notification(event: &Event, weather: &Weather) -> Result<String> { let icon = crate::emoji::emoji_for_weather(weather.weather_code)?; let name = event.name.as_deref().unwrap_or("Event"); let when = event.when.format("%b %-d, %H:%M").to_string(); let temp = format!("{:.0}°", weather.temperature); let feels = format!("{:.0}°", weather.feels_like); let probability = weather.probability_of_precipitation; let humidity = weather.humidity; let wind = format!("{:.0} km/h", weather.wind_speed); let wind_dir = crate::weather::wind_direction(weather.wind_direction) .map(|dir| format!(" {dir}")) .unwrap_or_default(); let location_display = if event.location.trim().is_empty() { format!("{:.4}°, {:.4}°", event.latitude, event.longitude) } else { event.location.clone() }; Ok(format!( "🗓️ {name} ({when}) 📍 {}\n{}{temp} (feels like {feels}) ☔ {probability}% rain · {humidity}% humidity 💨{wind}{wind_dir}", location_display, icon ))}
pub fn alt_text(event: &Event, weather: &Weather) -> String { let before = event.before.unwrap_or(crate::weather::DEFAULT_BEFORE); let after = event.after.unwrap_or(crate::weather::DEFAULT_AFTER); let window_start = (event.when - chrono::Duration::hours(before)) .format("%H:%M") .to_string(); let window_end = (event.when + chrono::Duration::hours(after)) .format("%H:%M") .to_string(); let first = weather.forecast.first(); let last = weather.forecast.last(); let temp_start = first.map_or(weather.temperature, |forecast| forecast.temperature); let temp_end = last.map_or(weather.temperature, |forecast| forecast.temperature); let precip_start = first.map_or(0, |forecast| forecast.probability_of_precipitation); let precip_end = last.map_or(0, |forecast| forecast.probability_of_precipitation); let wind_start = first.map_or(weather.wind_speed, |forecast| forecast.wind_speed); let wind_dir_start = crate::weather::wind_direction( first.map_or(weather.wind_direction, |forecast| forecast.wind_direction), ) .unwrap_or_default(); let wind_end = last.map_or(weather.wind_speed, |forecast| forecast.wind_speed); let wind_dir_end = crate::weather::wind_direction( last.map_or(weather.wind_direction, |forecast| forecast.wind_direction), ) .unwrap_or_default(); let mut min_temp = weather.temperature; let mut max_temp = weather.temperature; let mut min_prob = i8::MAX; let mut max_prob = 0i8; let mut min_wind = weather.wind_speed; let mut max_wind = weather.wind_speed; let mut wind_dir_min = wind_dir_start.clone(); let mut wind_dir_max = wind_dir_start.clone(); for forecast in &weather.forecast { if forecast.temperature < min_temp { min_temp = forecast.temperature; } if forecast.temperature > max_temp { max_temp = forecast.temperature; } if forecast.probability_of_precipitation < min_prob { min_prob = forecast.probability_of_precipitation; } if forecast.probability_of_precipitation > max_prob { max_prob = forecast.probability_of_precipitation; } if forecast.wind_speed < min_wind { min_wind = forecast.wind_speed; wind_dir_min = crate::weather::wind_direction(forecast.wind_direction).unwrap_or_default(); } if forecast.wind_speed > max_wind { max_wind = forecast.wind_speed; wind_dir_max = crate::weather::wind_direction(forecast.wind_direction).unwrap_or_default(); } } let location = if event.location.trim().is_empty() { String::new() } else { format!(" in {}", event.location) }; format!( "Hourly forecast for {}{} on {} ({}–{}). Temperature: {:.0}° at start, {:.0}° at end, {:.0}° min, {:.0}° max. Precipitation: {}% at start, {}% at end, {}% min, {}% max. Wind: {:.0} km/h {} at start, {:.0} km/h {} at end, {:.0} km/h {} min, {:.0} km/h {} max.", event.name.as_deref().unwrap_or("Event"), location, event.when.format("%b %-d, %H:%M"), window_start, window_end, temp_start, temp_end, min_temp, max_temp, precip_start, precip_end, min_prob, max_prob, wind_start, wind_dir_start, wind_end, wind_dir_end, min_wind, wind_dir_min, max_wind, wind_dir_max )}