const std = @import("std"); const Allocator = std.mem.Allocator; pub const Input = struct { allocator: Allocator, lines: []const []const u8 }; pub const Result = union(enum) { uint: u64, int: i64, fn value(self: Result, comptime T: type) ?T { inline for (@typeInfo(Result).Union.fields, 0..) |field, i| { if (field.type != T) continue; if (@intFromEnum(self) == i) { return @field(self, field.name); } } return null; } }; pub const Solver = *const fn(input: *Input) anyerror!Result; pub const Day = struct { part1: ?Solver, part2: ?Solver }; fn expectAnswer(comptime T: type, solver: Solver, expected: T, lines: []const []const u8) !void { var input = Input{ .lines = lines, .allocator = std.testing.allocator }; var result: Result = try solver(&input); const actual = result.value(T) orelse return error.ResultTypeMismatch; try std.testing.expectEqual(expected, actual); } pub fn expectAnswerUInt(solver: Solver, answer: u64, lines: []const []const u8) !void { try expectAnswer(u64, solver, answer, lines); } pub fn expectAnswerInt(solver: Solver, answer: i64, lines: []const []const u8) !void { try expectAnswer(i64, solver, answer, lines); }