const std = @import("std"); const types = @import("../weather/types.zig"); pub fn render(allocator: std.mem.Allocator, data: types.WeatherData, format: []const u8, use_imperial: bool) ![]const u8 { if (std.mem.eql(u8, format, "1")) { const temp = if (use_imperial) data.current.temp_f else data.current.temp_c; const unit = if (use_imperial) "°F" else "°C"; return std.fmt.allocPrint(allocator, "{s}: {s} {d:.0}{s}", .{ data.location, getConditionEmoji(data.current.weather_code), temp, unit, }); } else if (std.mem.eql(u8, format, "2")) { const temp = if (use_imperial) data.current.temp_f else data.current.temp_c; const unit = if (use_imperial) "°F" else "°C"; const wind = if (use_imperial) data.current.wind_kph * 0.621371 else data.current.wind_kph; const wind_unit = if (use_imperial) "mph" else "km/h"; return std.fmt.allocPrint(allocator, "{s}: {s} {d:.0}{s} {s}{s}{d:.0}{s}", .{ data.location, getConditionEmoji(data.current.weather_code), temp, unit, "🌬️", data.current.wind_dir, wind, wind_unit, }); } else if (std.mem.eql(u8, format, "3")) { const temp = if (use_imperial) data.current.temp_f else data.current.temp_c; const unit = if (use_imperial) "°F" else "°C"; const wind = if (use_imperial) data.current.wind_kph * 0.621371 else data.current.wind_kph; const wind_unit = if (use_imperial) "mph" else "km/h"; return std.fmt.allocPrint(allocator, "{s}: {s} {d:.0}{s} {s}{s}{d:.0}{s} {s}{d}%%", .{ data.location, getConditionEmoji(data.current.weather_code), temp, unit, "🌬️", data.current.wind_dir, wind, wind_unit, "💧", data.current.humidity, }); } else if (std.mem.eql(u8, format, "4")) { const temp = if (use_imperial) data.current.temp_f else data.current.temp_c; const unit = if (use_imperial) "°F" else "°C"; const wind = if (use_imperial) data.current.wind_kph * 0.621371 else data.current.wind_kph; const wind_unit = if (use_imperial) "mph" else "km/h"; return std.fmt.allocPrint(allocator, "{s}: {s} {d:.0}{s} {s}{s}{d:.0}{s} {s}{d}%% {s}", .{ data.location, getConditionEmoji(data.current.weather_code), temp, unit, "🌬️", data.current.wind_dir, wind, wind_unit, "💧", data.current.humidity, "☀️", }); } else { return renderCustom(allocator, data, format, use_imperial); } } fn renderCustom(allocator: std.mem.Allocator, data: types.WeatherData, format: []const u8, use_imperial: bool) ![]const u8 { var output: std.ArrayList(u8) = .empty; errdefer output.deinit(allocator); var i: usize = 0; while (i < format.len) { if (format[i] == '%' and i + 1 < format.len) { const code = format[i + 1]; switch (code) { 'c' => try output.appendSlice(allocator, getConditionEmoji(data.current.weather_code)), 'C' => try output.appendSlice(allocator, data.current.condition), 'h' => try output.writer(allocator).print("{d}", .{data.current.humidity}), 't' => { const temp = if (use_imperial) data.current.temp_f else data.current.temp_c; try output.writer(allocator).print("{d:.0}", .{temp}); }, 'f' => { const temp = if (use_imperial) data.current.temp_f else data.current.temp_c; try output.writer(allocator).print("{d:.0}", .{temp}); }, 'w' => { const wind = if (use_imperial) data.current.wind_kph * 0.621371 else data.current.wind_kph; const wind_unit = if (use_imperial) "mph" else "km/h"; try output.writer(allocator).print("{s}{d:.0}{s}", .{ data.current.wind_dir, wind, wind_unit }); }, 'l' => try output.appendSlice(allocator, data.location), 'p' => { const precip = if (use_imperial) data.current.precip_mm * 0.0393701 else data.current.precip_mm; try output.writer(allocator).print("{d:.1}", .{precip}); }, 'P' => { const pressure = if (use_imperial) data.current.pressure_mb * 0.02953 else data.current.pressure_mb; try output.writer(allocator).print("{d:.0}", .{pressure}); }, '%' => try output.append(allocator, '%'), else => { try output.append(allocator, '%'); try output.append(allocator, code); }, } i += 2; } else { try output.append(allocator, format[i]); i += 1; } } return output.toOwnedSlice(allocator); } fn getConditionEmoji(code: types.WeatherCode) []const u8 { return switch (@intFromEnum(code)) { 800 => "☀️", // Clear 801, 802 => "⛅️", // Few/scattered clouds 803, 804 => "☁️", // Broken/overcast clouds 701, 741 => "🌁", // Mist/fog (alternatively, maybe 🌫️ or 🌫 is better) 300...321 => "🌦", // Drizzle 500...531 => "🌧️", // Rain 200...232 => "⛈️", // Thunderstorm 611...616 => "❄️", // Sleet/freezing (check before snow) 600...610, 617...622 => "🌨️", // Snow else => "🌡️", }; } test "format 1" { const data = types.WeatherData{ .location = "London", .current = .{ .temp_c = 15.0, .temp_f = 59.0, .condition = "Clear", .weather_code = .clear, .humidity = 65, .wind_kph = 10.0, .wind_dir = "N", .pressure_mb = 1013.0, .precip_mm = 0.0, }, .forecast = &.{}, .allocator = std.testing.allocator, }; const output = try render(std.testing.allocator, data, "1", false); defer std.testing.allocator.free(output); try std.testing.expectEqualStrings("London: ☀️ 15°C", output); } test "custom format" { const data = types.WeatherData{ .location = "London", .current = .{ .temp_c = 15.0, .temp_f = 59.0, .condition = "Clear", .weather_code = .clear, .humidity = 65, .wind_kph = 10.0, .wind_dir = "N", .pressure_mb = 1013.0, .precip_mm = 0.0, }, .forecast = &.{}, .allocator = std.testing.allocator, }; const output = try render(std.testing.allocator, data, "%l: %c %t°C", false); defer std.testing.allocator.free(output); try std.testing.expectEqualStrings("London: ☀️ 15°C", output); } test "format 2 with imperial units" { const data = types.WeatherData{ .location = "Portland", .current = .{ .temp_c = 10.0, .temp_f = 50.0, .condition = "Cloudy", .weather_code = .clouds_overcast, .humidity = 70, .wind_kph = 20.0, .wind_dir = "SE", .pressure_mb = 1013.0, .precip_mm = 0.0, }, .forecast = &.{}, .allocator = std.testing.allocator, }; const output = try render(std.testing.allocator, data, "2", true); defer std.testing.allocator.free(output); try std.testing.expectEqualStrings("Portland: ☁️ 50°F 🌬️SE12mph", output); }