diff --git a/src/cli.zig b/src/cli.zig index fdf6356..1de5e46 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -2,13 +2,18 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const Self = @This(); +const Action = enum { Run, GetInput }; + allocator: Allocator, + +action: Action, day: u32, part: u32, input_file: []u8, fn print_usage(program: []const u8) void { - std.debug.print("Usage: {s} [input.txt]\n", .{program}); + std.debug.print("Usage: {s} [input.txt] Run solution\n", .{program}); + std.debug.print(" {s} get-input [input.txt] Get input. AOC_SESSSION environment variable needs to be defined\n", .{program}); } pub fn init(allocator: Allocator) !Self { @@ -17,6 +22,7 @@ pub fn init(allocator: Allocator) !Self { const program = try allocator.dupe(u8, args.next().?); defer allocator.free(program); + var action = Action.Run; var day: u32 = 0; var part: u32 = 0; var input_file = try allocator.dupe(u8, "input.txt"); @@ -24,29 +30,47 @@ pub fn init(allocator: Allocator) !Self { var i: u32 = 0; while (args.next()) |arg| { - if (i == 0) { - day = std.fmt.parseInt(u32, arg, 10) catch { - std.debug.print("ERROR: Invalid day '{s}'\n", .{arg}); - return error.cli; - }; - } else if (i == 1) { - part = std.fmt.parseInt(u32, arg, 10) catch { - std.debug.print("ERROR: Invalid part '{s}'\n", .{arg}); - return error.cli; - }; + if (i == 0 and std.mem.eql(u8, arg[0..], "get-input")) { + action = .GetInput; + } - if (!(part == 1 or part == 2)) { - std.debug.print("ERROR: Part can only be 1 or 2\n", .{}); - return error.cli; + if (action == .Run) { + if (i == 0) { + day = std.fmt.parseInt(u32, arg, 10) catch { + std.debug.print("ERROR: Invalid day '{s}'\n", .{arg}); + return error.cli; + }; + } else if (i == 1) { + part = std.fmt.parseInt(u32, arg, 10) catch { + std.debug.print("ERROR: Invalid part '{s}'\n", .{arg}); + return error.cli; + }; + + if (!(part == 1 or part == 2)) { + std.debug.print("ERROR: Part can only be 1 or 2\n", .{}); + return error.cli; + } + } else if (i == 2) { + allocator.free(input_file); + input_file = try allocator.dupe(u8, arg); + } + } else { + if (i == 1) { + day = std.fmt.parseInt(u32, arg, 10) catch { + std.debug.print("ERROR: Invalid day '{s}'\n", .{arg}); + return error.cli; + }; + } + if (i == 2) { + allocator.free(input_file); + input_file = try allocator.dupe(u8, arg); } - } else if (i == 2) { - allocator.free(input_file); - input_file = try allocator.dupe(u8, arg); } i += 1; } + if (i < 2) { print_usage(program); return error.cli; @@ -54,6 +78,7 @@ pub fn init(allocator: Allocator) !Self { return Self{ .allocator = allocator, + .action = action, .day = day, .part = part, .input_file = input_file diff --git a/src/main.zig b/src/main.zig index 1debd7e..be07d51 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,6 +2,7 @@ const std = @import("std"); const aoc = @import("./aoc.zig"); const cli = @import("./cli.zig"); +const Allocator = std.mem.Allocator; const StringArray = std.ArrayList([]const u8); const DayHashMap = std.StringHashMap(aoc.Day); @@ -27,19 +28,11 @@ const Days = [_]aoc.Day{ create_day(@import("day5.zig")), }; -pub fn main() !u8 { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const allocator = gpa.allocator(); - defer { - if (gpa.deinit() == .leak) @panic("Leaked memory"); - } - - var args = cli.init(allocator) catch |err| switch (err) { - error.cli => return 255, - else => return err - } ; - defer args.deinit(); +fn kilobytes(count: u32) u32 { + return count * 1024; +} +fn run(allocator: Allocator, args: *cli) !u8 { if (args.day > Days.len) { std.debug.print("ERROR: Day {} does not exist\n", .{args.day}); return 255; @@ -98,6 +91,81 @@ pub fn main() !u8 { return 0; } +fn get_input(allocator: Allocator, args: *cli) !u8 { + var client = std.http.Client{ .allocator = allocator }; + defer client.deinit(); + + var headers = std.http.Headers{ .allocator = allocator }; + defer headers.deinit(); + + if (!try std.process.hasEnvVar(allocator, "AOC_SESSION")) { + std.debug.print("ERROR: Missing environment variable AOC_SESSION\n", .{}); + return 255; + } + + var aoc_session = try std.process.getEnvVarOwned(allocator, "AOC_SESSION"); + defer allocator.free(aoc_session); + + var cookie_header = try std.fmt.allocPrint(allocator, "session={s}", .{aoc_session}); + defer allocator.free(cookie_header); + + try headers.append("cookie", cookie_header); + try headers.append("accept", "*/*"); + + var url_str = try std.fmt.allocPrint(allocator, "https://adventofcode.com/2023/day/{}/input", .{args.day}); + defer allocator.free(url_str); + + const uri = std.Uri.parse(url_str) catch unreachable; + + var request = try client.request(.GET, uri, headers, .{}); + defer request.deinit(); + + try request.start(); + try request.wait(); + + if (request.response.status != .ok) { + if (request.response.status == .not_found) { + std.debug.print("ERROR: Day not found\n", .{}); + } else if (request.response.status == .bad_request) { + std.debug.print("ERROR: Invalid session token\n", .{}); + } else { + std.debug.print("ERROR: unknown error, status code {}\n", .{request.response.status}); + } + + return 255; + } + + const body = try request.reader().readAllAlloc(allocator, kilobytes(8)); + defer allocator.free(body); + + var file = try std.fs.cwd().createFile(args.input_file, .{ }); + defer file.close(); + + try file.writeAll(body); + + return 0; +} + +pub fn main() !u8 { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + defer { + if (gpa.deinit() == .leak) @panic("Leaked memory"); + } + + var args = cli.init(allocator) catch |err| switch (err) { + error.cli => return 255, + else => return err + } ; + defer args.deinit(); + + if (args.action == .Run) { + return run(allocator, &args); + } else { + return get_input(allocator, &args); + } +} + test { _ = @import("day1.zig"); _ = @import("day2.zig");