Add ability to easily update template

This commit is contained in:
Martin Wickham 2022-11-26 14:35:05 -06:00
parent cdf752a35a
commit 4902734f1f
30 changed files with 290 additions and 26 deletions

View file

@ -18,6 +18,10 @@ This repo also contains Visual Studio Code project files for debugging. These a
If you would like to contribute project files for other development environments, please send a PR.
## Modifying the template
You can modify the template to add your own changes across all days. To do so, modify template/template.zig and then run `zig build generate`. The `$` character in the template will be replaced by the two-digit day number (e.g. 04 or 17). This step will only overwrite files which have not been modified, so you will not lose work if you update the template after implementing several days. After updating the template and generating, you should check in template/hashes.bin in addition to the updated template and source files. This will ensure that the newly generated files are not considered modified if you update the template again.
## Setting up ZLS
Zig has a reasonably robust language server, which can provide autocomplete for VSCode and many other editors. It can help significantly with exploring the std lib and suggesting parameter completions. The VSCode extension (augusterame.zls-vscode) will automatically install the language server in the background. If you are using a different editor, follow their [install instructions](https://zigtools.github.io/install-zls/). If you want to install a specific version of the language server (for example for maximum compatibility with 0.10.0), [check their releases page](https://github.com/zigtools/zls/releases) or [follow their instructions to build from source](https://github.com/zigtools/zls#from-source). Note that ZLS tracks master, so if you are using Zig 0.10.0 you may need to download a newer version to build ZLS.

View file

@ -41,6 +41,13 @@ pub fn build(b: *Builder) void {
const install_all_tests = b.step("install_tests_all", "Install tests for all days");
const run_all = b.step("run_all", "Run all days");
const generate = b.step("generate", "Generate stub files from template/template.zig");
const build_generate = b.addExecutable("generate", "template/generate.zig");
build_generate.setBuildMode(.ReleaseSafe);
const run_generate = build_generate.run();
run_generate.cwd = std.fs.path.dirname(@src().file).?;
generate.dependOn(&run_generate.step);
// Set up an exe for each day
var day: u32 = 1;
while (day <= 25) : (day += 1) {

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -12,7 +11,7 @@ const gpa = util.gpa;
const data = @embedFile("../data/day01.txt");
pub fn main() !void {
}
// Useful stdlib functions
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

View file

@ -4,7 +4,6 @@ const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const Str = []const u8;
const util = @import("util.zig");
const gpa = util.gpa;
@ -42,3 +41,7 @@ const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

126
template/generate.zig Normal file
View file

@ -0,0 +1,126 @@
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", .{});
}
}

5
template/hashes.bin Normal file
View file

@ -0,0 +1,5 @@
½<EFBFBD>½‰gŵ<uZêY1f¦”sŒÈy€Qƒƒ>‹Áœ:†òYöøâì³ì£¡cÂ:Å<>G2O¢&¶rs<72>%c<>ŒÒ¨}ƒÅõçú·2u,
è=z¯#âó Ã/ù!D¤²4&]ö‘×®wî*q(
¸îD”+YÂW^s(ª4¸DžM¸Ž„)î¥<C3AE>.™ÎPJ‡o«×uÁZü\Øãué ÎÜõþ÷Šÿù€6¥(”A%ž Žˆyëlø<6C>Ž´ 6J¤ ¬ ÜÃK~O"-oÿ©ºœÄȃ¬H;3ø s<%ò[:É/º<>À„b"YP†âÉé *_qd%!\VgQÒ-š4µ<34>}?7Ü
AKÓs¤£”¡Þ:vK,SÌ"Ëb…_-!#J}´Píh™¢G® à7<>7ѿχ×õ¹å—M 6©b
¸diŽÄš³Î\ËV¬y9ƒ`q¤d¼Òf¶­^bñ¦o_Ì.@Ñ`¿ð

47
template/template.zig Normal file
View file

@ -0,0 +1,47 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const List = std.ArrayList;
const Map = std.AutoHashMap;
const StrMap = std.StringHashMap;
const BitSet = std.DynamicBitSet;
const util = @import("util.zig");
const gpa = util.gpa;
const data = @embedFile("../data/day$.txt");
pub fn main() !void {
}
// Useful stdlib functions
const tokenize = std.mem.tokenize;
const split = std.mem.split;
const indexOf = std.mem.indexOfScalar;
const indexOfAny = std.mem.indexOfAny;
const indexOfStr = std.mem.indexOfPosLinear;
const lastIndexOf = std.mem.lastIndexOfScalar;
const lastIndexOfAny = std.mem.lastIndexOfAny;
const lastIndexOfStr = std.mem.lastIndexOfLinear;
const trim = std.mem.trim;
const sliceMin = std.mem.min;
const sliceMax = std.mem.max;
const parseInt = std.fmt.parseInt;
const parseFloat = std.fmt.parseFloat;
const min = std.math.min;
const min3 = std.math.min3;
const max = std.math.max;
const max3 = std.math.max3;
const print = std.debug.print;
const assert = std.debug.assert;
const sort = std.sort.sort;
const asc = std.sort.asc;
const desc = std.sort.desc;
// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.