diff --git a/src/render/ansi.zig b/src/render/ansi.zig index a9eccd8..f17c1cc 100644 --- a/src/render/ansi.zig +++ b/src/render/ansi.zig @@ -376,7 +376,7 @@ test "unknown weather code art" { .temp_c = 16.0, .temp_f = 61.0, .condition = "Unknown", - .weather_code = @enumFromInt(999), + .weather_code = .unknown, .humidity = 60, .wind_kph = 10.0, .wind_dir = "N", diff --git a/src/weather/MetNo.zig b/src/weather/MetNo.zig index 834d7dd..ac05677 100644 --- a/src/weather/MetNo.zig +++ b/src/weather/MetNo.zig @@ -5,6 +5,61 @@ const types = @import("types.zig"); const MetNo = @This(); +const MetNoOpenWeatherEntry = struct { []const u8, types.WeatherCode }; +// symbol codes: https://github.com/metno/weathericons/tree/main/weather +// they also have _day, _night and _polartwilight variants +// +// Openweathermap weather condition codes: +// https://openweathermap.org/weather-conditions +const weather_code_entries = [_]MetNoOpenWeatherEntry{ + // zig fmt: off + .{ "clearsky", .clear }, + .{ "cloudy", .clouds_overcast }, + .{ "fair", .clouds_few }, + .{ "fog", .fog }, + .{ "heavyrain", .rain_heavy }, + .{ "heavyrainandthunder", .thunderstorm_heavy_rain }, + .{ "heavyrainshowers", .thunderstorm_drizzle }, + .{ "heavyrainshowersandthunder", .thunderstorm_heavy_drizzle }, + .{ "heavysleet", .sleet }, + .{ "heavysleetandthunder", .thunderstorm_heavy_rain }, + .{ "heavysleetshowers", .sleet_shower }, + .{ "heavysleetshowersandthunder", .thunderstorm_heavy_drizzle }, + .{ "heavysnow", .snow_heavy }, + .{ "heavysnowandthunder", .thunderstorm_heavy }, + .{ "heavysnowshowers", .snow_shower_heavy }, + .{ "heavysnowshowersandthunder", .thunderstorm_heavy }, + .{ "lightrain", .rain_light }, + .{ "lightrainandthunder", .thunderstorm_light_rain }, + .{ "lightrainshowers", .rain_shower_light }, + .{ "lightrainshowersandthunder", .thunderstorm_light_rain }, + .{ "lightsleet", .sleet_shower_light }, + .{ "lightsleetandthunder", .thunderstorm_light }, + .{ "lightsleetshowers", .sleet_shower_light }, + .{ "lightsnow", .snow_light }, + .{ "lightsnowandthunder", .thunderstorm_light }, + .{ "lightsnowshowers", .snow_shower_light }, + .{ "lightssleetshowersandthunder", .thunderstorm_light }, + .{ "lightssnowshowersandthunder", .thunderstorm_light }, + .{ "partlycloudy", .clouds_few }, + .{ "rain", .rain_moderate }, + .{ "rainandthunder", .thunderstorm_rain }, + .{ "rainshowers", .rain_shower }, + .{ "rainshowersandthunder", .thunderstorm_drizzle }, + .{ "sleet", .sleet }, + .{ "sleetandthunder", .thunderstorm }, + .{ "sleetshowers", .sleet_shower }, + .{ "sleetshowersandthunder", .thunderstorm_drizzle }, + .{ "snow", .snow }, + .{ "snowandthunder", .thunderstorm }, + .{ "snowshowers", .snow_shower }, + .{ "snowshowersandthunder", .thunderstorm }, + // zig fmt: on + }; + +const WeatherCodeMap = std.StaticStringMap(types.WeatherCode); +const weather_code_map = WeatherCodeMap.initComptime(weather_code_entries); + allocator: std.mem.Allocator, pub fn init(allocator: std.mem.Allocator) !MetNo { @@ -127,29 +182,26 @@ fn parseMetNoResponse(allocator: std.mem.Allocator, coords: Coordinates, json: s } fn symbolCodeToWeatherCode(symbol: []const u8) types.WeatherCode { - if (std.mem.indexOf(u8, symbol, "clearsky")) |_| return .clear; - if (std.mem.indexOf(u8, symbol, "partlycloudy")) |_| return .clouds_scattered; - if (std.mem.indexOf(u8, symbol, "fair")) |_| return .clouds_few; - if (std.mem.indexOf(u8, symbol, "cloudy")) |_| return .clouds_overcast; - if (std.mem.indexOf(u8, symbol, "fog")) |_| return .fog; - if (std.mem.indexOf(u8, symbol, "thunder")) |_| return .thunderstorm; - if (std.mem.indexOf(u8, symbol, "rain")) |_| return .rain_moderate; - if (std.mem.indexOf(u8, symbol, "sleet")) |_| return .sleet; - if (std.mem.indexOf(u8, symbol, "snow")) |_| return .snow; - return .clear; + var it = std.mem.splitScalar(u8, symbol, '_'); + const metno_weather_code = it.next().?; + const variant = it.next(); + _ = variant; // discard day/night/polar twilight for now + return weather_code_map.get(metno_weather_code) orelse .unknown; } fn symbolCodeToCondition(symbol: []const u8) []const u8 { - if (std.mem.indexOf(u8, symbol, "clearsky")) |_| return "Clear"; - if (std.mem.indexOf(u8, symbol, "partlycloudy")) |_| return "Partly cloudy"; - if (std.mem.indexOf(u8, symbol, "fair")) |_| return "Fair"; - if (std.mem.indexOf(u8, symbol, "cloudy")) |_| return "Cloudy"; - if (std.mem.indexOf(u8, symbol, "fog")) |_| return "Fog"; - if (std.mem.indexOf(u8, symbol, "thunder")) |_| return "Thunderstorm"; - if (std.mem.indexOf(u8, symbol, "rain")) |_| return "Rain"; - if (std.mem.indexOf(u8, symbol, "sleet")) |_| return "Sleet"; - if (std.mem.indexOf(u8, symbol, "snow")) |_| return "Snow"; - return "Clear"; + var it = std.mem.splitScalar(u8, symbol, '_'); + const metno_weather_code = it.next().?; + if (std.mem.eql(u8, metno_weather_code, "clearsky")) return "Clear"; + if (std.mem.eql(u8, metno_weather_code, "partlycloudy")) return "Partly cloudy"; + if (std.mem.eql(u8, metno_weather_code, "fair")) return "Fair"; + if (std.mem.eql(u8, metno_weather_code, "cloudy")) return "Cloudy"; + if (std.mem.eql(u8, metno_weather_code, "fog")) return "Fog"; + if (std.mem.eql(u8, metno_weather_code, "thunder")) return "Thunderstorm"; + if (std.mem.eql(u8, metno_weather_code, "rain")) return "Rain"; + if (std.mem.eql(u8, metno_weather_code, "sleet")) return "Sleet"; + if (std.mem.eql(u8, metno_weather_code, "snow")) return "Snow"; + return "Unknown"; } fn degreeToDirection(deg: f32) []const u8 { @@ -174,6 +226,6 @@ test "symbolCodeToWeatherCode" { try std.testing.expectEqual(types.WeatherCode.clear, symbolCodeToWeatherCode("clearsky_day")); try std.testing.expectEqual(types.WeatherCode.clouds_few, symbolCodeToWeatherCode("fair_night")); try std.testing.expectEqual(types.WeatherCode.clouds_overcast, symbolCodeToWeatherCode("cloudy")); - try std.testing.expectEqual(types.WeatherCode.rain_moderate, symbolCodeToWeatherCode("lightrain")); + try std.testing.expectEqual(types.WeatherCode.rain_light, symbolCodeToWeatherCode("lightrain")); try std.testing.expectEqual(types.WeatherCode.snow, symbolCodeToWeatherCode("snow")); } diff --git a/src/weather/types.zig b/src/weather/types.zig index cc8af54..ddd7e06 100644 --- a/src/weather/types.zig +++ b/src/weather/types.zig @@ -72,7 +72,7 @@ pub const WeatherCode = enum(u16) { clouds_broken = 803, // 51-84% clouds_overcast = 804, // 85-100% - _, + unknown = 999, }; pub const WeatherError = error{