const std = @import("std"); const INPUTS_DIR = "inputs"; const SESSION_FILE = "session.txt"; const MAX_INPUT_SIZE = 512 * 512; // Maximum allowed input size (bytes) pub const Options = struct { year: usize, days: usize, }; pub fn fetchInputs(allocator: std.mem.Allocator, opts: Options) !void { const cwd = std.fs.cwd(); cwd.makeDir(INPUTS_DIR) catch |err| { switch (err) { error.PathAlreadyExists => {}, else => return err, } }; const session = cwd.readFileAlloc(allocator, SESSION_FILE, 256) catch |err| { std.log.err("Unable to open '{s}'.\nMake sure to copy the session cookie value to this file to download the user specific input files.", .{SESSION_FILE}); return err; }; defer allocator.free(session); for (1..opts.days + 1) |day| { const file_name = try std.fmt.allocPrint(allocator, "{s}/day{d:0>2}.txt", .{ INPUTS_DIR, day }); defer allocator.free(file_name); const fd = cwd.openFile(file_name, std.fs.File.OpenFlags{}) catch |err| { switch (err) { error.FileNotFound => { try fetchDay(allocator, opts, session, day); continue; }, else => return err, } }; defer fd.close(); } } fn fetchDay(allocator: std.mem.Allocator, opts: Options, session: []const u8, day: usize) !void { var client = std.http.Client{ .allocator = allocator }; defer client.deinit(); std.debug.print("Downloading day{d:0>2}\r", .{day}); const url = try std.fmt.allocPrint( allocator, "https://adventofcode.com/{d}/day/{d}/input", .{ opts.year, day }, ); defer allocator.free(url); const uri = try std.Uri.parse(url); var headers = std.ArrayList(std.http.Header).init(allocator); defer headers.deinit(); try headers.append(.{ .name = "Cookie", .value = try std.fmt.allocPrint(allocator, "session={s}", .{session}), }); defer { for (headers.items) |header| { allocator.free(header.value); } } var sh: [512]u8 = undefined; var req = try client.open(.GET, uri, .{ .server_header_buffer = &sh, .extra_headers = headers.items, }); defer req.deinit(); try req.send(); try req.finish(); try req.wait(); const body = try req.reader().readAllAlloc(allocator, MAX_INPUT_SIZE); defer allocator.free(body); if (req.response.status != std.http.Status.ok) { std.log.err("{}", .{req.response.status}); std.log.err("{s}", .{body}); return error.HttpRequestError; } const file_name = try std.fmt.allocPrint(allocator, "{s}/day{d:0>2}.txt", .{ INPUTS_DIR, day }); defer allocator.free(file_name); try std.fs.cwd().writeFile(std.fs.Dir.WriteFileOptions{ .sub_path = file_name, .data = body, .flags = .{ .mode = 0o644 }, }); } /// Checks if `INPUTS_DIR` exists and all the input files are available. pub fn checkInputs(days: usize) bool { const cwd = std.fs.cwd(); const inputs = cwd.openDir(INPUTS_DIR, std.fs.Dir.OpenDirOptions{ .iterate = true, }) catch { return false; }; var counter: usize = 0; var it = inputs.iterate(); while (it.next() catch unreachable) |_| : (counter += 1) {} if (counter == days) return true else return false; }