wttr/src/render/Line.zig
2026-01-06 17:12:38 -08:00

197 lines
7.6 KiB
Zig
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const std = @import("std");
const types = @import("../weather/types.zig");
const emoji = @import("emoji.zig");
const utils = @import("utils.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.tempFahrenheit() 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,
emoji.getWeatherEmoji(data.current.weather_code),
temp,
unit,
});
} else if (std.mem.eql(u8, format, "2")) {
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
const unit = if (use_imperial) "°F" else "°C";
const wind = if (use_imperial) data.current.windMph() 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,
emoji.getWeatherEmoji(data.current.weather_code),
temp,
unit,
"🌬️",
utils.degreeToDirection(data.current.wind_deg),
wind,
wind_unit,
});
} else if (std.mem.eql(u8, format, "3")) {
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
const unit = if (use_imperial) "°F" else "°C";
const wind = if (use_imperial) data.current.windMph() 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,
emoji.getWeatherEmoji(data.current.weather_code),
temp,
unit,
"🌬️",
utils.degreeToDirection(data.current.wind_deg),
wind,
wind_unit,
"💧",
data.current.humidity,
});
} else if (std.mem.eql(u8, format, "4")) {
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
const unit = if (use_imperial) "°F" else "°C";
const wind = if (use_imperial) data.current.windMph() 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,
emoji.getWeatherEmoji(data.current.weather_code),
temp,
unit,
"🌬️",
utils.degreeToDirection(data.current.wind_deg),
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, emoji.getWeatherEmoji(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.tempFahrenheit() else data.current.temp_c;
try output.writer(allocator).print("{d:.0}", .{temp});
},
'f' => {
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
try output.writer(allocator).print("{d:.0}", .{temp});
},
'w' => {
const wind = if (use_imperial) data.current.windMph() else data.current.wind_kph;
const wind_unit = if (use_imperial) "mph" else "km/h";
try output.writer(allocator).print("{s}{d:.0}{s}", .{ utils.degreeToDirection(data.current.wind_deg), 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);
}
test "format 1" {
const data = types.WeatherData{
.location = "London",
.coords = .{ .latitude = 0, .longitude = 0 },
.current = .{
.temp_c = 15.0,
.feels_like_c = 15.0,
.condition = "Clear",
.weather_code = .clear,
.humidity = 65,
.wind_kph = 10.0,
.wind_deg = 0.0,
.pressure_mb = 1013.0,
.precip_mm = 0.0,
.visibility_km = null,
},
.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",
.coords = .{ .latitude = 0, .longitude = 0 },
.current = .{
.temp_c = 15.0,
.feels_like_c = 15.0,
.condition = "Clear",
.weather_code = .clear,
.humidity = 65,
.wind_kph = 10.0,
.wind_deg = 0.0,
.pressure_mb = 1013.0,
.precip_mm = 0.0,
.visibility_km = null,
},
.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",
.coords = .{ .latitude = 0, .longitude = 0 },
.current = .{
.temp_c = 10.0,
.feels_like_c = 10.0,
.condition = "Cloudy",
.weather_code = .clouds_overcast,
.humidity = 70,
.wind_kph = 20.0,
.wind_deg = 135.0,
.pressure_mb = 1013.0,
.precip_mm = 0.0,
.visibility_km = null,
},
.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);
}