126 lines
No EOL
4.5 KiB
Zig
126 lines
No EOL
4.5 KiB
Zig
const std = @import("std");
|
|
|
|
const max_size = 100_000_000;
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
|
|
const Hash = std.crypto.hash.Md5;
|
|
const hashes_file = "template/hashes.bin";
|
|
|
|
fn instantiateTemplate(template: []const u8, day: u32) ![]const u8 {
|
|
var list = std.ArrayList(u8).init(gpa.allocator());
|
|
errdefer list.deinit();
|
|
|
|
try list.ensureTotalCapacity(template.len + 100);
|
|
var rest: []const u8 = template;
|
|
while (std.mem.indexOfScalar(u8, rest, '$')) |index| {
|
|
try list.appendSlice(rest[0..index]);
|
|
try std.fmt.format(list.writer(), "{d:0>2}", .{day});
|
|
rest = rest[index+1..];
|
|
}
|
|
try list.appendSlice(rest);
|
|
return list.toOwnedSlice();
|
|
}
|
|
|
|
fn readHashes() !*[25][Hash.digest_length]u8 {
|
|
const hash_bytes = std.fs.cwd().readFileAlloc(gpa.allocator(), hashes_file, 25 * Hash.digest_length) catch |err| switch (err) {
|
|
error.FileTooBig => return error.InvalidFormat,
|
|
else => |e| return e,
|
|
};
|
|
errdefer gpa.allocator().free(hash_bytes);
|
|
|
|
if (hash_bytes.len != 25 * Hash.digest_length)
|
|
return error.InvalidFormat;
|
|
|
|
return @ptrCast(*[25][Hash.digest_length]u8, hash_bytes.ptr);
|
|
}
|
|
|
|
pub fn main() !void {
|
|
const template = try std.fs.cwd().readFileAlloc(gpa.allocator(), "template/template.zig", max_size);
|
|
|
|
const hashes: *[25][Hash.digest_length]u8 = readHashes() catch |err| switch (err) {
|
|
error.FileNotFound => blk: {
|
|
std.debug.print("{s} doesn't exist, will assume all files have been modified.\nDelete src/dayXX.zig and rerun `zig build generate` to regenerate it.\n", .{hashes_file});
|
|
const mem = try gpa.allocator().create([25][Hash.digest_length]u8);
|
|
@memset(@ptrCast([*]u8, mem), 0, @sizeOf(@TypeOf(mem.*)));
|
|
break :blk mem;
|
|
},
|
|
error.InvalidFormat => {
|
|
std.debug.print("{s} is corrupted, delete it to silence this warning and assume all days have been modified.\n", .{hashes_file});
|
|
std.os.exit(1);
|
|
},
|
|
else => |e| {
|
|
std.debug.print("Failed to open {s}: {}\n", .{hashes_file, e});
|
|
return e;
|
|
},
|
|
};
|
|
|
|
var skipped_any = false;
|
|
var updated_hashes = false;
|
|
var day: u32 = 1;
|
|
while (day <= 25) : (day += 1) {
|
|
const filename = try std.fmt.allocPrint(gpa.allocator(), "src/day{d:0>2}.zig", .{day});
|
|
defer gpa.allocator().free(filename);
|
|
|
|
var new_file = false;
|
|
const file = std.fs.cwd().openFile(filename, .{.mode = .read_write}) catch |err| switch (err) {
|
|
error.FileNotFound => blk: {
|
|
new_file = true;
|
|
break :blk try std.fs.cwd().createFile(filename, .{});
|
|
},
|
|
else => |e| return e,
|
|
};
|
|
defer file.close();
|
|
|
|
var regenerate = false;
|
|
if (!new_file) {
|
|
const contents = file.readToEndAlloc(gpa.allocator(), max_size) catch |err| switch (err) {
|
|
error.FileTooBig => {
|
|
std.debug.print("Skipping modified day {s}\n", .{filename});
|
|
skipped_any = true;
|
|
continue;
|
|
},
|
|
else => |e| return e,
|
|
};
|
|
defer gpa.allocator().free(contents);
|
|
|
|
var hash: [Hash.digest_length]u8 = undefined;
|
|
Hash.hash(contents, &hash, .{});
|
|
|
|
regenerate = std.mem.eql(u8, &hash, &hashes[day-1]);
|
|
} else {
|
|
regenerate = true;
|
|
}
|
|
|
|
if (regenerate) {
|
|
if (!new_file) {
|
|
try file.seekTo(0);
|
|
try file.setEndPos(0);
|
|
}
|
|
|
|
const text = try instantiateTemplate(template, day);
|
|
defer gpa.allocator().free(text);
|
|
|
|
Hash.hash(text, &hashes[day-1], .{});
|
|
updated_hashes = true;
|
|
|
|
try file.writeAll(text);
|
|
if (new_file) {
|
|
std.debug.print("Creating new file {s} from template.\n", .{filename});
|
|
} else {
|
|
std.debug.print("Updated {s}\n", .{filename});
|
|
}
|
|
} else {
|
|
std.debug.print("Skipping modified day {s}\n", .{filename});
|
|
skipped_any = true;
|
|
}
|
|
}
|
|
|
|
if (updated_hashes) {
|
|
try std.fs.cwd().writeFile(hashes_file, std.mem.asBytes(hashes));
|
|
if (skipped_any) {
|
|
std.debug.print("Some days were skipped. Delete them to force regeneration.\n",.{});
|
|
}
|
|
} else {
|
|
std.debug.print("No updates made, all days were modified. Delete src/dayXX.zig to force regeneration.\n", .{});
|
|
}
|
|
} |