Changes
3 changed files (+231/-8)
-
-
@@ -88,10 +88,150 @@ return ParseArgsError.UnknownArg;} }; const legacy_daylight_keywords: []const []const u8 = &.{ "sun", "day", "light", "normal", "visible", "daylight", }; const legacy_civil_keywords: []const []const u8 = &.{ "civil", "civ", }; const legacy_nautical_keywords: []const []const u8 = &.{ "nautical", "nau", "naut", }; const legacy_astronomical_keywords: []const []const u8 = &.{ "astronomical", "ast", "astr", "astro", }; const legacy_custom_angle_keywords: []const []const u8 = &.{ "a", "angle", "twilightangle", "twilight", }; const TwilightAngle = union(enum) { daylight: void, civil: void, nautical: void, astronomical: void, custom: f64, const valueless_variants: []const TwilightAngle = &.{ .daylight, .civil, .nautical, .astronomical }; pub fn parseArg(arg: []const u8, args: *std.process.ArgIterator) ParseArgsError!@This() { inline for (legacy_daylight_keywords) |keyword| { if (std.mem.eql(u8, keyword, arg)) { return .daylight; } } inline for (legacy_civil_keywords) |keyword| { if (std.mem.eql(u8, keyword, arg)) { return .civil; } } inline for (legacy_nautical_keywords) |keyword| { if (std.mem.eql(u8, keyword, arg)) { return .nautical; } } inline for (legacy_astronomical_keywords) |keyword| { if (std.mem.eql(u8, keyword, arg)) { return .astronomical; } } inline for (legacy_custom_angle_keywords) |keyword| { if (std.mem.eql(u8, keyword, arg)) { const next = args.next() orelse { std.log.err("{s} option requires a value", .{arg}); return ParseArgsError.MissingValue; }; const angle = std.fmt.parseFloat(f64, next) catch { std.log.err("Value of {s} must be valid floating point number", .{arg}); return ParseArgsError.InvalidAngle; }; return .{ .custom = angle }; } } if (std.mem.eql(u8, "--twilight", arg)) { const next = args.next() orelse { std.log.err("{s} option requires a value", .{arg}); return ParseArgsError.MissingValue; }; inline for (valueless_variants) |variant| { if (std.mem.eql(u8, @tagName(variant), next)) { return variant; } } return .{ .custom = std.fmt.parseFloat(f64, next) catch { const variants = comptime variants: { var buf_size: usize = 0; for (valueless_variants) |variant| { buf_size += @tagName(variant).len + 4; } var buf: [buf_size]u8 = undefined; var i: usize = 0; for (valueless_variants) |variant| { const wrote = std.fmt.bufPrint(buf[i..], "\"{s}\", ", .{@tagName(variant)}) catch { @compileError("Failed to write to comptime buffer"); }; i += wrote.len; } break :variants buf; }; std.log.err("Value of {s} must be {s}or a floating point number", .{ arg, variants, }); return ParseArgsError.InvalidAngle; }, }; } return ParseArgsError.UnknownArg; } pub fn toFloat(self: @This()) f64 { return switch (self) { .daylight => -50.0 / 60.0, .civil => -6.0, .nautical => -12.0, .astronomical => -18.0, .custom => |v| v, }; } }; latitude: ?f64 = null, longitude: ?f64 = null, offset_hour: f64 = 0, twilight_angle: f64 = c.TWILIGHT_ANGLE_DAYLIGHT, twilight_angle: ?TwilightAngle = null, utc: bool = false, debug: bool = false, command: CommandOptions = .poll,
-
@@ -105,6 +245,7 @@ InvalidDayOfMonth,InvalidMonth, InvalidYearSince2000, InvalidEventType, InvalidAngle, }; pub const ReportOptions = struct {
-
@@ -315,6 +456,21 @@ ParseArgsError.UnknownArg => {},else => return err, } if (TwilightAngle.parseArg(arg, args)) |angle| { if (self.twilight_angle) |prev| { std.log.warn("Overwriting twilight angle \"{s}\" with \"{s}\"", .{ @tagName(prev), @tagName(angle), }); } self.twilight_angle = angle; return; } else |err| switch (err) { ParseArgsError.UnknownArg => {}, else => return err, } if (std.mem.eql(u8, "--debug", arg)) { self.debug = true; return;
-
@@ -503,6 +659,8 @@ .list => |opts| opts.event_type orelse .both,else => .both, }; const twilight_angle = self.twilight_angle orelse .daylight; // This effectful function updates `c.timezone`. c.tzset();
-
@@ -510,7 +668,7 @@ return c.runStruct{.latitude = self.latitude orelse c.DEFAULT_LATITUDE, .longitude = self.longitude orelse c.DEFAULT_LONGITUDE, .offsetHour = self.offset_hour, .twilightAngle = self.twilight_angle, .twilightAngle = twilight_angle.toFloat(), .nowTimet = now, .targetTimet = target_time, .now2000 = c.daysSince2000(&now),
-
-
-
@@ -62,6 +62,14 @@ \\ Longitude of the point to calculate sunrise and sunset.\\ <DEGREE> must be a floating point number or a positive \\ floating point number with E or W suffix. \\ \\--twilight <TYPE OR ANGLE> \\ Specify twilight angle to determine day (twilight) or night. \\ Value must be a floating point number or one of: \\ * daylight \\ * civil \\ * nautical \\ * astronomical \\ \\[Commands] \\poll Prints whether it's DAY or NIGHT. \\wait Sleep until sunrise and/or sunset, then exits.
-
@@ -78,12 +86,6 @@ \\ You can specify this option multiple times to explicitly\\ tell sunwait to target both. \\ \\[Common Parameters] \\TWILIGHT Twilight type. Valid values are: \\ * daylight \\ * civil \\ * nautical \\ * astronomical \\ * angle <degree> \\OFFSET Time offset for sunrise and sunset time, towards noon. \\ Format must be "MM" (minutes) or "HH:MM". \\
-
-
-
@@ -27,6 +27,8 @@ latitude: []const u8 = "29.977435N",days: ?[]const u8 = "3", tz: []const u8 = "UTC", event: ?enum { sunset, sunrise } = null, twilight: ?[]const u8 = null, twilight_angle: ?[]const u8 = null, }; fn list(allocator: std.mem.Allocator, opts: ListOptions) !std.process.Child.RunResult {
-
@@ -51,6 +53,15 @@ try args.append(opts.longitude);if (opts.event) |e| { try args.append(@tagName(e)); } if (opts.twilight) |t| { try args.append(t); } if (opts.twilight_angle) |angle| { try args.append("angle"); try args.append(angle); } return try std.process.Child.run(.{
-
@@ -183,3 +194,55 @@ try std.testing.expectEqual(legacy.term.Exited, new.term.Exited);try std.testing.expectEqualStrings(legacy.stderr, new.stderr); try std.testing.expectEqualStrings(legacy.stdout, new.stdout); } test "Should behave same even with twilight angle set" { const legacy = try list(std.testing.allocator, .{ .bin = config.legacy_bin, .tz = "Europe/Paris", .utc = true, .event = .sunset, .twilight = "civil", }); defer std.testing.allocator.free(legacy.stderr); defer std.testing.allocator.free(legacy.stdout); const new = try list(std.testing.allocator, .{ .bin = config.new_bin, .tz = "Europe/Paris", .utc = true, .event = .sunset, .twilight = "civil", }); defer std.testing.allocator.free(new.stderr); defer std.testing.allocator.free(new.stdout); try std.testing.expectEqual(legacy.term.Exited, new.term.Exited); try std.testing.expectEqualStrings(legacy.stderr, new.stderr); try std.testing.expectEqualStrings(legacy.stdout, new.stdout); } test "Should behave same on custom twilight angle" { const legacy = try list(std.testing.allocator, .{ .bin = config.legacy_bin, .tz = "Europe/Paris", .utc = true, .event = .sunset, .twilight_angle = "2.1", }); defer std.testing.allocator.free(legacy.stderr); defer std.testing.allocator.free(legacy.stdout); const new = try list(std.testing.allocator, .{ .bin = config.new_bin, .tz = "Europe/Paris", .utc = true, .event = .sunset, .twilight_angle = "2.1", }); defer std.testing.allocator.free(new.stderr); defer std.testing.allocator.free(new.stdout); try std.testing.expectEqual(legacy.term.Exited, new.term.Exited); try std.testing.expectEqualStrings(legacy.stderr, new.stderr); try std.testing.expectEqualStrings(legacy.stdout, new.stdout); }
-