get one line output matching wttr.in
This commit is contained in:
parent
115af96c8a
commit
59ec75dbf6
4 changed files with 42 additions and 57 deletions
|
|
@ -425,7 +425,7 @@ test "handler: format line 1" {
|
||||||
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
||||||
|
|
||||||
try ht.expectStatus(200);
|
try ht.expectStatus(200);
|
||||||
try ht.expectBody("Test: ☀️ 20°C");
|
try ht.expectBody("☀️ +20°C");
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler: format line 2" {
|
test "handler: format line 2" {
|
||||||
|
|
@ -446,7 +446,7 @@ test "handler: format line 2" {
|
||||||
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
||||||
|
|
||||||
try ht.expectStatus(200);
|
try ht.expectStatus(200);
|
||||||
try ht.expectBody("Test: ☀️ 20°C 🌬️N5km/h");
|
try ht.expectBody("☀️ 🌡️+20°C 🌬️↓5km/h");
|
||||||
}
|
}
|
||||||
|
|
||||||
test "handler: format line 3" {
|
test "handler: format line 3" {
|
||||||
|
|
@ -467,5 +467,5 @@ test "handler: format line 3" {
|
||||||
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
||||||
|
|
||||||
try ht.expectStatus(200);
|
try ht.expectStatus(200);
|
||||||
try ht.expectBody("Test: ☀️ 20°C 🌬️N5km/h 💧50%%");
|
try ht.expectBody("Test: ☀️ +20°C");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const types = @import("../weather/types.zig");
|
const types = @import("../weather/types.zig");
|
||||||
const zeit = @import("zeit");
|
const zeit = @import("zeit");
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
|
||||||
/// Select 4 hours representing morning (6am), noon (12pm), evening (6pm), night (12am) in LOCAL time
|
/// Select 4 hours representing morning (6am), noon (12pm), evening (6pm), night (12am) in LOCAL time
|
||||||
/// Hours in the hourly forecast are assumed to be all on the same day, in local time
|
/// Hours in the hourly forecast are assumed to be all on the same day, in local time
|
||||||
|
|
@ -46,13 +47,6 @@ fn selectHourlyForecasts(all_hours: []const types.HourlyForecast, buf: []?types.
|
||||||
return selected.items;
|
return selected.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn degreeToArrow(deg: f32) []const u8 {
|
|
||||||
const normalized = @mod(deg + 22.5, 360.0);
|
|
||||||
const idx: usize = @intFromFloat(normalized / 45.0);
|
|
||||||
const arrows = [_][]const u8{ "↓", "↙", "←", "↖", "↑", "↗", "→", "↘" };
|
|
||||||
return arrows[@min(idx, 7)];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Format = enum {
|
pub const Format = enum {
|
||||||
plain_text,
|
plain_text,
|
||||||
ansi,
|
ansi,
|
||||||
|
|
@ -146,7 +140,7 @@ fn renderCurrent(w: *std.Io.Writer, current: types.CurrentCondition, options: Re
|
||||||
.plain_text => {
|
.plain_text => {
|
||||||
try w.print("{s} {s}\n", .{ art[0], current.condition });
|
try w.print("{s} {s}\n", .{ art[0], current.condition });
|
||||||
try w.print("{s} {c}{d:.0}({c}{d:.0}) {s}\n", .{ art[1], sign, abs_temp, fl_sign, abs_fl, temp_unit });
|
try w.print("{s} {c}{d:.0}({c}{d:.0}) {s}\n", .{ art[1], sign, abs_temp, fl_sign, abs_fl, temp_unit });
|
||||||
try w.print("{s} {s} {d:.0} {s}\n", .{ art[2], degreeToArrow(current.wind_deg), wind_speed, wind_unit });
|
try w.print("{s} {s} {d:.0} {s}\n", .{ art[2], utils.degreeToArrow(current.wind_deg), wind_speed, wind_unit });
|
||||||
if (current.visibility_km) |_| {
|
if (current.visibility_km) |_| {
|
||||||
const visibility = if (options.use_imperial) current.visiblityMph().? else current.visibility_km.?;
|
const visibility = if (options.use_imperial) current.visiblityMph().? else current.visibility_km.?;
|
||||||
const vis_unit = if (options.use_imperial) "mi" else "km";
|
const vis_unit = if (options.use_imperial) "mi" else "km";
|
||||||
|
|
@ -163,7 +157,7 @@ fn renderCurrent(w: *std.Io.Writer, current: types.CurrentCondition, options: Re
|
||||||
|
|
||||||
try w.print("{s} {s}\n", .{ art[0], current.condition });
|
try w.print("{s} {s}\n", .{ art[0], current.condition });
|
||||||
try w.print("{s} \x1b[38;5;{d}m{c}{d:.0}({c}{d:.0}){s} {s}\n", .{ art[1], temp_color_code, sign, abs_temp, fl_sign, abs_fl, reset, temp_unit });
|
try w.print("{s} \x1b[38;5;{d}m{c}{d:.0}({c}{d:.0}){s} {s}\n", .{ art[1], temp_color_code, sign, abs_temp, fl_sign, abs_fl, reset, temp_unit });
|
||||||
try w.print("{s} {s} \x1b[38;5;{d}m{d:.0}{s} {s}\n", .{ art[2], degreeToArrow(current.wind_deg), wind_color_code, wind_speed, reset, wind_unit });
|
try w.print("{s} {s} \x1b[38;5;{d}m{d:.0}{s} {s}\n", .{ art[2], utils.degreeToArrow(current.wind_deg), wind_color_code, wind_speed, reset, wind_unit });
|
||||||
if (current.visibility_km) |_| {
|
if (current.visibility_km) |_| {
|
||||||
const visibility = if (options.use_imperial) current.visiblityMph().? else current.visibility_km.?;
|
const visibility = if (options.use_imperial) current.visiblityMph().? else current.visibility_km.?;
|
||||||
const vis_unit = if (options.use_imperial) "mi" else "km";
|
const vis_unit = if (options.use_imperial) "mi" else "km";
|
||||||
|
|
@ -179,7 +173,7 @@ fn renderCurrent(w: *std.Io.Writer, current: types.CurrentCondition, options: Re
|
||||||
|
|
||||||
try w.print("{s} {s}\n", .{ art[0], current.condition });
|
try w.print("{s} {s}\n", .{ art[0], current.condition });
|
||||||
try w.print("{s} <span style=\"color:{s}\">{c}{d:.0}({c}{d:.0})</span> {s}\n", .{ art[1], temp_color, sign, abs_temp, fl_sign, abs_fl, temp_unit });
|
try w.print("{s} <span style=\"color:{s}\">{c}{d:.0}({c}{d:.0})</span> {s}\n", .{ art[1], temp_color, sign, abs_temp, fl_sign, abs_fl, temp_unit });
|
||||||
try w.print("{s} {s} <span style=\"color:{s}\">{d:.0}</span> {s}\n", .{ art[2], degreeToArrow(current.wind_deg), wind_color, wind_speed, wind_unit });
|
try w.print("{s} {s} <span style=\"color:{s}\">{d:.0}</span> {s}\n", .{ art[2], utils.degreeToArrow(current.wind_deg), wind_color, wind_speed, wind_unit });
|
||||||
if (current.visibility_km) |_| {
|
if (current.visibility_km) |_| {
|
||||||
const visibility = if (options.use_imperial) current.visiblityMph().? else current.visibility_km.?;
|
const visibility = if (options.use_imperial) current.visiblityMph().? else current.visibility_km.?;
|
||||||
const vis_unit = if (options.use_imperial) "mi" else "km";
|
const vis_unit = if (options.use_imperial) "mi" else "km";
|
||||||
|
|
@ -329,7 +323,7 @@ fn renderHourlyCell(w: *std.Io.Writer, hour: types.HourlyForecast, line: usize,
|
||||||
.wind => {
|
.wind => {
|
||||||
const wind_speed = if (options.use_imperial) hour.windMph() else hour.wind_kph;
|
const wind_speed = if (options.use_imperial) hour.windMph() else hour.wind_kph;
|
||||||
const wind_unit = if (options.use_imperial) "mph" else "km/h";
|
const wind_unit = if (options.use_imperial) "mph" else "km/h";
|
||||||
const arrow = degreeToArrow(hour.wind_deg);
|
const arrow = utils.degreeToArrow(hour.wind_deg);
|
||||||
switch (options.format) {
|
switch (options.format) {
|
||||||
.ansi => {
|
.ansi => {
|
||||||
const color = windColor(hour.wind_kph);
|
const color = windColor(hour.wind_kph);
|
||||||
|
|
|
||||||
|
|
@ -11,68 +11,52 @@ const Format = enum(u3) {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn render(writer: *std.Io.Writer, data: types.WeatherData, format: Format, use_imperial: bool) !void {
|
pub fn render(writer: *std.Io.Writer, data: types.WeatherData, format: Format, use_imperial: bool) !void {
|
||||||
|
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
|
||||||
|
const unit = if (use_imperial) "°F" else "°C";
|
||||||
|
const sign: []const u8 = if (temp >= 0) "+" else if (temp < 0) "-" else "";
|
||||||
|
const abs_temp = @abs(temp);
|
||||||
|
const wind = if (use_imperial) data.current.windMph() else data.current.wind_kph;
|
||||||
|
const wind_unit = if (use_imperial) "mph" else "km/h";
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
.@"1" => {
|
.@"1" => {
|
||||||
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
|
try writer.print("{s} {s}{d:.0}{s}", .{
|
||||||
const unit = if (use_imperial) "°F" else "°C";
|
|
||||||
try writer.print("{s}: {s} {d:.0}{s}", .{
|
|
||||||
data.location,
|
|
||||||
emoji.getWeatherEmoji(data.current.weather_code),
|
emoji.getWeatherEmoji(data.current.weather_code),
|
||||||
temp,
|
sign,
|
||||||
|
abs_temp,
|
||||||
unit,
|
unit,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
.@"2" => {
|
.@"2" => {
|
||||||
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
|
try writer.print("{s} 🌡️{s}{d:.0}{s} 🌬️{s}{d:.0}{s}", .{
|
||||||
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";
|
|
||||||
try writer.print("{s}: {s} {d:.0}{s} {s}{s}{d:.0}{s}", .{
|
|
||||||
data.location,
|
|
||||||
emoji.getWeatherEmoji(data.current.weather_code),
|
emoji.getWeatherEmoji(data.current.weather_code),
|
||||||
temp,
|
sign,
|
||||||
|
abs_temp,
|
||||||
unit,
|
unit,
|
||||||
"🌬️",
|
utils.degreeToArrow(data.current.wind_deg),
|
||||||
utils.degreeToDirection(data.current.wind_deg),
|
|
||||||
wind,
|
wind,
|
||||||
wind_unit,
|
wind_unit,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
.@"3" => {
|
.@"3" => {
|
||||||
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
|
try writer.print("{s}: {s} {s}{d:.0}{s}", .{
|
||||||
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";
|
|
||||||
try writer.print("{s}: {s} {d:.0}{s} {s}{s}{d:.0}{s} {s}{d}%%", .{
|
|
||||||
data.location,
|
data.location,
|
||||||
emoji.getWeatherEmoji(data.current.weather_code),
|
emoji.getWeatherEmoji(data.current.weather_code),
|
||||||
temp,
|
sign,
|
||||||
|
abs_temp,
|
||||||
unit,
|
unit,
|
||||||
"🌬️",
|
|
||||||
utils.degreeToDirection(data.current.wind_deg),
|
|
||||||
wind,
|
|
||||||
wind_unit,
|
|
||||||
"💧",
|
|
||||||
data.current.humidity,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
.@"4" => {
|
.@"4" => {
|
||||||
const temp = if (use_imperial) data.current.tempFahrenheit() else data.current.temp_c;
|
try writer.print("{s}: {s} 🌡️{s}{d:.0}{s} 🌬️{s}{d:.0}{s}", .{
|
||||||
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";
|
|
||||||
try writer.print("{s}: {s} {d:.0}{s} {s}{s}{d:.0}{s} {s}{d}%% {s}", .{
|
|
||||||
data.location,
|
data.location,
|
||||||
emoji.getWeatherEmoji(data.current.weather_code),
|
emoji.getWeatherEmoji(data.current.weather_code),
|
||||||
temp,
|
sign,
|
||||||
|
abs_temp,
|
||||||
unit,
|
unit,
|
||||||
"🌬️",
|
utils.degreeToArrow(data.current.wind_deg),
|
||||||
utils.degreeToDirection(data.current.wind_deg),
|
|
||||||
wind,
|
wind,
|
||||||
wind_unit,
|
wind_unit,
|
||||||
"💧",
|
|
||||||
data.current.humidity,
|
|
||||||
"☀️",
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +89,7 @@ test "format 1" {
|
||||||
|
|
||||||
const output = output_buf[0..writer.end];
|
const output = output_buf[0..writer.end];
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("London: ☀️ 15°C", output);
|
try std.testing.expectEqualStrings("☀️ +15°C", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "format 2 with imperial units" {
|
test "format 2 with imperial units" {
|
||||||
|
|
@ -116,7 +100,7 @@ test "format 2 with imperial units" {
|
||||||
|
|
||||||
const output = output_buf[0..writer.end];
|
const output = output_buf[0..writer.end];
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("London: ☀️ 59°F 🌬️N6mph", output);
|
try std.testing.expectEqualStrings("☀️ 🌡️+59°F 🌬️↓6mph", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "format 3 with metric units" {
|
test "format 3 with metric units" {
|
||||||
|
|
@ -127,7 +111,7 @@ test "format 3 with metric units" {
|
||||||
|
|
||||||
const output = output_buf[0..writer.end];
|
const output = output_buf[0..writer.end];
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("London: ☀️ 15°C 🌬️N10km/h 💧65%%", output);
|
try std.testing.expectEqualStrings("London: ☀️ +15°C", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "format 3 with imperial units" {
|
test "format 3 with imperial units" {
|
||||||
|
|
@ -138,7 +122,7 @@ test "format 3 with imperial units" {
|
||||||
|
|
||||||
const output = output_buf[0..writer.end];
|
const output = output_buf[0..writer.end];
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("London: ☀️ 59°F 🌬️N6mph 💧65%%", output);
|
try std.testing.expectEqualStrings("London: ☀️ +59°F", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "format 4 with metric units" {
|
test "format 4 with metric units" {
|
||||||
|
|
@ -149,7 +133,7 @@ test "format 4 with metric units" {
|
||||||
|
|
||||||
const output = output_buf[0..writer.end];
|
const output = output_buf[0..writer.end];
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("London: ☀️ 15°C 🌬️N10km/h 💧65%% ☀️", output);
|
try std.testing.expectEqualStrings("London: ☀️ 🌡️+15°C 🌬️↓10km/h", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "format 4 with imperial units" {
|
test "format 4 with imperial units" {
|
||||||
|
|
@ -160,5 +144,5 @@ test "format 4 with imperial units" {
|
||||||
|
|
||||||
const output = output_buf[0..writer.end];
|
const output = output_buf[0..writer.end];
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("London: ☀️ 59°F 🌬️N6mph 💧65%% ☀️", output);
|
try std.testing.expectEqualStrings("London: ☀️ 🌡️+59°F 🌬️↓6mph", output);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,13 @@ pub fn degreeToDirection(deg: f32) []const u8 {
|
||||||
return directions[@min(idx, 7)];
|
return directions[@min(idx, 7)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn degreeToArrow(deg: f32) []const u8 {
|
||||||
|
const normalized = @mod(deg + 22.5, 360.0);
|
||||||
|
const idx: usize = @intFromFloat(normalized / 45.0);
|
||||||
|
const arrows = [_][]const u8{ "↓", "↙", "←", "↖", "↑", "↗", "→", "↘" };
|
||||||
|
return arrows[@min(idx, 7)];
|
||||||
|
}
|
||||||
|
|
||||||
test "degreeToDirection" {
|
test "degreeToDirection" {
|
||||||
try std.testing.expectEqualStrings("N", degreeToDirection(0));
|
try std.testing.expectEqualStrings("N", degreeToDirection(0));
|
||||||
try std.testing.expectEqualStrings("NE", degreeToDirection(45));
|
try std.testing.expectEqualStrings("NE", degreeToDirection(45));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue