wttr/zig/src/render/custom.zig

137 lines
4.8 KiB
Zig

const std = @import("std");
const types = @import("../weather/types.zig");
const weather_icons = [_][]const u8{
"", // 0-99
"🌑", // 100-199
"", // 200-299
"☁️", // 300-399
};
fn getWeatherIcon(code: u16) []const u8 {
const idx = @min(code / 100, weather_icons.len - 1);
return weather_icons[idx];
}
pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format: []const u8) ![]const u8 {
var output: std.ArrayList(u8) = .empty;
errdefer output.deinit(allocator);
const writer = output.writer(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 writer.print("{s}", .{getWeatherIcon(weather.current.weather_code)}),
'C' => try writer.print("{s}", .{weather.current.condition}),
'h' => try writer.print("{d}%", .{weather.current.humidity}),
't' => try writer.print("{d:.0}°C", .{weather.current.temp_c}),
'f' => try writer.print("{d:.0}°C", .{weather.current.temp_c}), // Feels like (same as temp for now)
'w' => try writer.print("{d:.0} km/h {s}", .{ weather.current.wind_kph, weather.current.wind_dir }),
'l' => try writer.print("{s}", .{weather.location}),
'p' => try writer.print("{d:.1} mm", .{weather.current.precip_mm}),
'P' => try writer.print("{d:.0} hPa", .{weather.current.pressure_mb}),
'm' => try writer.print("🌕", .{}), // Moon phase placeholder
'M' => try writer.print("15", .{}), // Moon day placeholder
'o' => try writer.print("0%", .{}), // Probability of precipitation placeholder
'D' => try writer.print("06:00", .{}), // Dawn placeholder
'S' => try writer.print("07:30", .{}), // Sunrise placeholder
'z' => try writer.print("12:00", .{}), // Zenith placeholder
's' => try writer.print("18:30", .{}), // Sunset placeholder
'd' => try writer.print("20:00", .{}), // Dusk placeholder
'%' => try writer.print("%", .{}),
'n' => try writer.print("\n", .{}),
else => {
try writer.print("%{c}", .{code});
},
}
i += 2;
} else {
try writer.writeByte(format[i]);
i += 1;
}
}
return output.toOwnedSlice(allocator);
}
test "render custom format with location and temp" {
const allocator = std.testing.allocator;
const weather = types.WeatherData{
.location = "London",
.current = .{
.temp_c = 7.0,
.temp_f = 44.6,
.condition = "Overcast",
.weather_code = 122,
.humidity = 76,
.wind_kph = 11.0,
.wind_dir = "NNE",
.pressure_mb = 1019.0,
.precip_mm = 0.0,
},
.forecast = &[_]types.ForecastDay{},
.allocator = allocator,
};
const output = try render(allocator, weather, "%l: %c %t");
defer allocator.free(output);
try std.testing.expect(std.mem.indexOf(u8, output, "London") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "7°C") != null);
}
test "render custom format with newline" {
const allocator = std.testing.allocator;
const weather = types.WeatherData{
.location = "Paris",
.current = .{
.temp_c = 10.0,
.temp_f = 50.0,
.condition = "Clear",
.weather_code = 113,
.humidity = 65,
.wind_kph = 8.0,
.wind_dir = "E",
.pressure_mb = 1020.0,
.precip_mm = 0.0,
},
.forecast = &[_]types.ForecastDay{},
.allocator = allocator,
};
const output = try render(allocator, weather, "%l%n%C");
defer allocator.free(output);
try std.testing.expect(std.mem.indexOf(u8, output, "Paris\nClear") != null);
}
test "render custom format with humidity and pressure" {
const allocator = std.testing.allocator;
const weather = types.WeatherData{
.location = "Berlin",
.current = .{
.temp_c = 5.0,
.temp_f = 41.0,
.condition = "Cloudy",
.weather_code = 119,
.humidity = 85,
.wind_kph = 12.0,
.wind_dir = "W",
.pressure_mb = 1012.0,
.precip_mm = 0.2,
},
.forecast = &[_]types.ForecastDay{},
.allocator = allocator,
};
const output = try render(allocator, weather, "Humidity: %h, Pressure: %P");
defer allocator.free(output);
try std.testing.expect(std.mem.indexOf(u8, output, "85%") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "1012") != null);
}