git.lucas.co / cce-weather
weather: now, the next 24 hours and the week
git clone https://git.lucas.co/cce-weather.git

src/glyph.rs (3.8K)

  1 //! WMO weather codes: their wording, and the glyph for each — one of the
  2 //! multicolour `weather-*` glyphs of the cce-icons set, drawn through
  3 //! cce-ui's `PaintCtx::icon_untinted`.
  4 
  5 use cce_ui::scene::layout::Rect;
  6 use cce_ui::scene::paint::PaintCtx;
  7 
  8 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  9 pub enum Sky {
 10     Clear,
 11     PartlyCloudy,
 12     Cloudy,
 13     Fog,
 14     Drizzle,
 15     Rain,
 16     Snow,
 17     Thunder,
 18 }
 19 
 20 /// WMO 4677 code as Open-Meteo reports it → (sky, words).
 21 pub fn describe(code: u8) -> (Sky, &'static str) {
 22     match code {
 23         0 => (Sky::Clear, "Clear"),
 24         1 => (Sky::Clear, "Mainly clear"),
 25         2 => (Sky::PartlyCloudy, "Partly cloudy"),
 26         3 => (Sky::Cloudy, "Overcast"),
 27         45 => (Sky::Fog, "Fog"),
 28         48 => (Sky::Fog, "Freezing fog"),
 29         51 => (Sky::Drizzle, "Light drizzle"),
 30         53 => (Sky::Drizzle, "Drizzle"),
 31         55 => (Sky::Drizzle, "Heavy drizzle"),
 32         56 | 57 => (Sky::Drizzle, "Freezing drizzle"),
 33         61 => (Sky::Rain, "Light rain"),
 34         63 => (Sky::Rain, "Rain"),
 35         65 => (Sky::Rain, "Heavy rain"),
 36         66 | 67 => (Sky::Rain, "Freezing rain"),
 37         71 => (Sky::Snow, "Light snow"),
 38         73 => (Sky::Snow, "Snow"),
 39         75 => (Sky::Snow, "Heavy snow"),
 40         77 => (Sky::Snow, "Snow grains"),
 41         80 => (Sky::Rain, "Light showers"),
 42         81 => (Sky::Rain, "Showers"),
 43         82 => (Sky::Rain, "Violent showers"),
 44         85 | 86 => (Sky::Snow, "Snow showers"),
 45         95 => (Sky::Thunder, "Thunderstorm"),
 46         96 | 99 => (Sky::Thunder, "Thunderstorm, hail"),
 47         _ => (Sky::Cloudy, "Unknown"),
 48     }
 49 }
 50 
 51 /// The cce-icons glyph for `sky`. `day` picks sun or moon for the clear and
 52 /// partly-cloudy skies; the rest have one glyph for day and night alike.
 53 pub fn icon_name(sky: Sky, day: bool) -> &'static str {
 54     match (sky, day) {
 55         (Sky::Clear, true) => "weather-clear-day",
 56         (Sky::Clear, false) => "weather-clear-night",
 57         (Sky::PartlyCloudy, true) => "weather-partly-cloudy-day",
 58         (Sky::PartlyCloudy, false) => "weather-partly-cloudy-night",
 59         (Sky::Cloudy, _) => "weather-cloudy",
 60         (Sky::Fog, _) => "weather-fog",
 61         (Sky::Drizzle, _) => "weather-drizzle",
 62         (Sky::Rain, _) => "weather-rain",
 63         (Sky::Snow, _) => "weather-snow",
 64         (Sky::Thunder, _) => "weather-thunder",
 65     }
 66 }
 67 
 68 /// Draw the glyph for `code` filling the square `rect`. The weather glyphs
 69 /// carry their own colours, so they are drawn untinted.
 70 pub fn draw(pc: &mut PaintCtx, rect: Rect, code: u8, day: bool) {
 71     let (sky, _) = describe(code);
 72     pc.icon_untinted(icon_name(sky, day), rect, 1.0);
 73 }
 74 
 75 #[cfg(test)]
 76 mod tests {
 77     use super::*;
 78 
 79     #[test]
 80     fn every_documented_code_has_words() {
 81         for code in [0, 1, 2, 3, 45, 48, 51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 71, 73, 75, 77, 80, 81, 82, 85, 86, 95, 96, 99] {
 82             assert_ne!(describe(code).1, "Unknown", "code {code}");
 83         }
 84         assert_eq!(describe(42).1, "Unknown");
 85     }
 86 
 87     /// Every glyph named is one the cce-icons set ships.
 88     #[test]
 89     fn every_sky_names_a_shipped_glyph() {
 90         let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../cce-icons/svg");
 91         if !dir.is_dir() {
 92             eprintln!("skipping: no cce-icons checkout beside this crate");
 93             return;
 94         }
 95         let skies = [Sky::Clear, Sky::PartlyCloudy, Sky::Cloudy, Sky::Fog, Sky::Drizzle, Sky::Rain, Sky::Snow, Sky::Thunder];
 96         for sky in skies {
 97             for day in [true, false] {
 98                 let name = icon_name(sky, day);
 99                 assert!(dir.join(format!("{name}.svg")).is_file(), "{sky:?} day={day}: no {name}.svg");
100             }
101         }
102         assert_ne!(icon_name(Sky::Clear, true), icon_name(Sky::Clear, false));
103         assert_ne!(icon_name(Sky::PartlyCloudy, true), icon_name(Sky::PartlyCloudy, false));
104     }
105 }