184 lines
7.1 KiB
Zig
184 lines
7.1 KiB
Zig
const std = @import("std");
|
|
const types = @import("../weather/types.zig");
|
|
const emoji = @import("emoji.zig");
|
|
|
|
pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format: []const u8, use_imperial: bool) ![]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}", .{emoji.getWeatherEmoji(weather.current.weather_code)}),
|
|
'C' => try writer.print("{s}", .{weather.current.condition}),
|
|
'h' => try writer.print("{d}%", .{weather.current.humidity}),
|
|
't' => {
|
|
const temp = if (use_imperial) weather.current.tempFahrenheit() else weather.current.temp_c;
|
|
const unit = if (use_imperial) "°F" else "°C";
|
|
const sign: u8 = if (temp >= 0) '+' else 0;
|
|
if (sign != 0) {
|
|
try writer.print("{c}{d:.1}{s}", .{ sign, temp, unit });
|
|
} else {
|
|
try writer.print("{d:.1}{s}", .{ temp, unit });
|
|
}
|
|
},
|
|
'f' => {
|
|
const temp = if (use_imperial) weather.current.tempFahrenheit() else weather.current.temp_c;
|
|
const unit = if (use_imperial) "°F" else "°C";
|
|
const sign: u8 = if (temp >= 0) '+' else 0;
|
|
if (sign != 0) {
|
|
try writer.print("{c}{d:.1}{s}", .{ sign, temp, unit });
|
|
} else {
|
|
try writer.print("{d:.1}{s}", .{ temp, unit });
|
|
}
|
|
},
|
|
'w' => {
|
|
const wind = if (use_imperial) weather.current.wind_kph * 0.621371 else weather.current.wind_kph;
|
|
const unit = if (use_imperial) "mph" else "km/h";
|
|
try writer.print("{d:.0} {s} {s}", .{ wind, unit, weather.current.wind_dir });
|
|
},
|
|
'l' => try writer.print("{s}", .{weather.location}),
|
|
'p' => {
|
|
const precip = if (use_imperial) weather.current.precip_mm * 0.0393701 else weather.current.precip_mm;
|
|
const unit = if (use_imperial) "in" else "mm";
|
|
try writer.print("{d:.1} {s}", .{ precip, unit });
|
|
},
|
|
'P' => {
|
|
const pressure = if (use_imperial) weather.current.pressure_mb * 0.02953 else weather.current.pressure_mb;
|
|
const unit = if (use_imperial) "inHg" else "hPa";
|
|
try writer.print("{d:.2} {s}", .{ pressure, unit });
|
|
},
|
|
'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,
|
|
.feels_like_c = 7.0,
|
|
.condition = "Overcast",
|
|
.weather_code = .clouds_overcast,
|
|
.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", false);
|
|
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.0°C") != null);
|
|
}
|
|
|
|
test "render custom format with newline" {
|
|
const allocator = std.testing.allocator;
|
|
|
|
const weather = types.WeatherData{
|
|
.location = "Paris",
|
|
.current = .{
|
|
.temp_c = 10.0,
|
|
.feels_like_c = 10.0,
|
|
.condition = "Clear",
|
|
.weather_code = .clear,
|
|
.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", false);
|
|
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,
|
|
.feels_like_c = 5.0,
|
|
.condition = "Cloudy",
|
|
.weather_code = .clouds_overcast,
|
|
.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", false);
|
|
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);
|
|
}
|
|
|
|
test "render custom format with imperial units" {
|
|
const allocator = std.testing.allocator;
|
|
|
|
const weather = types.WeatherData{
|
|
.location = "NYC",
|
|
.current = .{
|
|
.temp_c = 10.0,
|
|
.feels_like_c = 10.0,
|
|
.condition = "Clear",
|
|
.weather_code = .clear,
|
|
.humidity = 60,
|
|
.wind_kph = 16.0,
|
|
.wind_dir = "N",
|
|
.pressure_mb = 1013.0,
|
|
.precip_mm = 2.5,
|
|
},
|
|
.forecast = &[_]types.ForecastDay{},
|
|
.allocator = allocator,
|
|
};
|
|
|
|
const output = try render(allocator, weather, "%t %w %p", true);
|
|
defer allocator.free(output);
|
|
|
|
try std.testing.expect(std.mem.indexOf(u8, output, "+50.0°F") != null);
|
|
try std.testing.expect(std.mem.indexOf(u8, output, "mph") != null);
|
|
try std.testing.expect(std.mem.indexOf(u8, output, "in") != null);
|
|
}
|