From 64371401cb8f608e00c0c0f5460257d74e42166d Mon Sep 17 00:00:00 2001 From: Robert Günzler Date: Thu, 24 Sep 2026 18:48:35 +0200 Subject: also wrap Io.Dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Günzler --- src/main.zig | 144 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 105 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/main.zig b/src/main.zig index 04d1f41..90a8cb7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -140,17 +140,17 @@ const Cli = struct { // TODO: allow setting custom root path via flags const root_path = std.fs.path.dirname(source_path) orelse return error.NoDirname; - const root_dir = try Io.Dir.cwd().openDir(io, root_path, .{}); - defer root_dir.close(io); + const root_dir = try Dir.init(io, Io.Dir.cwd(), root_path, .{}); + defer root_dir.fd.close(io); var source = try File.init(io, root_dir, std.fs.path.basename(source_path), null); defer source.close(io); // open/create destination dir - const target_dir = try Io.Dir.cwd().createDirPathOpen(io, target_path, .{}); - defer target_dir.close(io); + const target_dir = try Dir.init(io, Io.Dir.cwd(), target_path, .{}); + defer target_dir.fd.close(io); - const b = try Builder.init(gpa, root_dir, source, target_dir); + const b = try Builder.init(gpa, root_dir, source, target_dir, .{}); defer b.deinit(gpa); try b.build(io, gpa); } @@ -174,12 +174,12 @@ const Cli = struct { log.info("gen : target={s} root={s}", .{ target_path, root_path }); - const root_dir = try Io.Dir.cwd().openDir(io, root_path, .{ .iterate = true }); - defer root_dir.close(io); + const root_dir = try Dir.init(io, Io.Dir.cwd(), root_path, .{ .iterate = true }); + defer root_dir.fd.close(io); // open/create destination dir - const target_dir = try Io.Dir.cwd().createDirPathOpen(io, target_path, .{}); - defer target_dir.close(io); + const target_dir = try Dir.init(io, Io.Dir.cwd(), target_path, .{}); + defer target_dir.fd.close(io); var work: std.ArrayList([]const u8) = .empty; defer work.deinit(gpa); @@ -187,7 +187,7 @@ const Cli = struct { var mdn = std.BufSet.init(gpa); defer mdn.deinit(); - var iter = root_dir.iterate(); + var iter = root_dir.fd.iterate(); while (try iter.next(io)) |child| { if (child.kind != .file) continue; @@ -240,7 +240,7 @@ const Cli = struct { var source = try File.init(io, root_dir, md_name, null); defer source.close(io); - const b = try Builder.init(gpa, root_dir, source, target_dir); + const b = try Builder.init(gpa, root_dir, source, target_dir, .{}); defer b.deinit(gpa); try b.build(io, gpa); } @@ -251,7 +251,7 @@ const Cli = struct { var source = try File.init(io, root_dir, path, null); defer source.close(io); - const b = try Builder.init(gpa, root_dir, source, target_dir); + const b = try Builder.init(gpa, root_dir, source, target_dir, .{}); defer b.deinit(gpa); try b.build(io, gpa); @@ -284,7 +284,7 @@ const Cli = struct { fn genBackrefs( io: Io, gpa: Allocator, - target_dir: Io.Dir, + target_dir: Dir, target_path: []const u8, work: std.ArrayList([]const u8), outgoingRefs: std.StringHashMapUnmanaged([][]const u8), @@ -367,8 +367,8 @@ const Cli = struct { fn genIndex( io: Io, gpa: Allocator, - root_dir: Io.Dir, - target_dir: Io.Dir, + root_dir: Dir, + target_dir: Dir, index: std.ArrayList([]const u8), ) !void { log.info("Generating index", .{}); @@ -380,7 +380,7 @@ const Cli = struct { }; defer source.close(io); - const b = try Builder.init(gpa, root_dir, source, target_dir); + const b = try Builder.init(gpa, root_dir, source, target_dir, .{}); b.index = index; defer b.deinit(gpa); try b.build(io, gpa); @@ -407,14 +407,27 @@ const Cli = struct { } }; +const Dir = struct { + path: []const u8, + fd: Io.Dir, + + fn init(io: Io, dir: Io.Dir, path: []const u8, options: Io.Dir.OpenOptions) !Dir { + return .{ + .path = path, + .fd = try dir.openDir(io, path, options), + }; + } +}; + const File = struct { path: []const u8, fd: Io.File, + dir: Dir, mmap: Io.File.MemoryMap, mtime: Io.Timestamp, - fn init(io: Io, dir: Io.Dir, path: []const u8, options: ?Io.Dir.OpenFileOptions) !File { - const fd = try dir.openFile(io, path, options orelse .{ + fn init(io: Io, dir: Dir, path: []const u8, options: ?Io.Dir.OpenFileOptions) !File { + const fd = try dir.fd.openFile(io, path, options orelse .{ .lock = .exclusive, .mode = .read_write, }); @@ -427,6 +440,7 @@ const File = struct { return .{ .path = path, .fd = fd, + .dir = dir, .mmap = mmap, .mtime = stat.mtime, }; @@ -449,9 +463,9 @@ const File = struct { }; const Builder = struct { - root_dir: Io.Dir, + root_dir: Dir, source: File, - target_dir: Io.Dir, + target_dir: Dir, options: Options, // track files which affect build output (paths relative to root_dir) @@ -472,13 +486,19 @@ const Builder = struct { const DELIM_START: []const u8 = "{["; const DELIM_END: []const u8 = "]}"; - fn init(gpa: Allocator, root_dir: Io.Dir, source: File, target_dir: Io.Dir) !*Builder { + fn init( + gpa: Allocator, + root_dir: Dir, + source: File, + target_dir: Dir, + options: Options, + ) !*Builder { const b = try gpa.create(Builder); b.* = .{ .root_dir = root_dir, .source = source, .target_dir = target_dir, - .options = .{}, + .options = options, }; return b; } @@ -524,7 +544,7 @@ const Builder = struct { // atomic? errdefer log.err("path={s}", .{target_path}); - const target_fd = try self.target_dir.createFile(io, target_path, .{ + const target_fd = try self.target_dir.fd.createFile(io, target_path, .{ .lock = .exclusive, }); defer target_fd.close(io); @@ -550,7 +570,11 @@ const Builder = struct { } // finalize computes the digest and attaches it to the target file - fn finalize(self: *Builder, gpa: Allocator, target_fd: Io.File) !void { + fn finalize( + self: *Builder, + gpa: Allocator, + target_fd: Io.File, + ) !void { log.debug("{s}: finalizing", .{self.source.path}); const composite_dgst = compositeDigest(self.dgsts.items); @@ -559,7 +583,13 @@ const Builder = struct { } // inject interprets source and writes to w - fn inject(self: *Builder, io: Io, gpa: Allocator, source: File, w: *Io.Writer) !void { + fn inject( + self: *Builder, + io: Io, + gpa: Allocator, + source: File, + w: *Io.Writer, + ) !void { var iter = std.mem.splitScalar(u8, source.mmap.memory, '\n'); while (iter.next()) |line| { var offset: usize = 0; @@ -590,7 +620,14 @@ const Builder = struct { try self.dgsts.append(gpa, try source.digest()); } - fn template(self: *Builder, io: Io, gpa: Allocator, ref_name: []const u8, w: *Io.Writer, track_refs: bool) anyerror!void { + fn template( + self: *Builder, + io: Io, + gpa: Allocator, + ref_name: []const u8, + w: *Io.Writer, + track_refs: bool, + ) anyerror!void { log.debug("{s}: templating ref={s}", .{ self.source.path, ref_name }); // special case for when we're injecting the source into the baseof @@ -657,7 +694,13 @@ const Builder = struct { } } - fn templatePortal(self: *Builder, io: Io, gpa: Allocator, ref_name: []const u8, w: *Io.Writer) !void { + fn templatePortal( + self: *Builder, + io: Io, + gpa: Allocator, + ref_name: []const u8, + w: *Io.Writer, + ) !void { log.debug("{s}: portaling ref={s}", .{ self.source.path, ref_name }); const ref_path = try std.fmt.allocPrint(gpa, "{s}.htm", .{ref_name}); @@ -671,7 +714,14 @@ const Builder = struct { try w.writeByte('\n'); } - fn templateRef(self: *Builder, io: Io, gpa: Allocator, ref_str: []const u8, w: *Io.Writer, track_refs: bool) !void { + fn templateRef( + self: *Builder, + io: Io, + gpa: Allocator, + ref_str: []const u8, + w: *Io.Writer, + track_refs: bool, + ) !void { const fragment = std.mem.indexOf(u8, ref_str, "#"); const ref = if (fragment) |i| ref_str[0..i] else ref_str; @@ -689,7 +739,7 @@ const Builder = struct { var found = false; for (possible_paths) |p| found = found or (blk: { - self.root_dir.access(io, p, .{}) catch break :blk false; + self.root_dir.fd.access(io, p, .{}) catch break :blk false; break :blk true; }); if (!found) { @@ -715,13 +765,23 @@ const Builder = struct { if (track_refs) try self.refs.append(gpa, try gpa.dupe(u8, ref)); } - fn templateLink(self: *Builder, gpa: Allocator, ref_name: []const u8, w: *std.Io.Writer) !void { + fn templateLink( + self: *Builder, + gpa: Allocator, + ref_name: []const u8, + w: *std.Io.Writer, + ) !void { const link = try formatLink(gpa, ref_name, self.options.offset_from_siteroot); defer gpa.free(link); try w.writeAll(link); } - fn templateName(_: *Builder, gpa: Allocator, ref_name: []const u8, w: *std.Io.Writer) !void { + fn templateName( + _: *Builder, + gpa: Allocator, + ref_name: []const u8, + w: *std.Io.Writer, + ) !void { const name = try formatName(gpa, ref_name); defer gpa.free(name); try w.writeAll(name); @@ -737,7 +797,7 @@ const Builder = struct { if (try targetExists(io, gpa, self.target_dir, target_path, self.root_dir)) return; // TODO: allow customizing tilde dir - const target_fd = try self.target_dir.createFile(io, target_path, .{ + const target_fd = try self.target_dir.fd.createFile(io, target_path, .{ .lock = .exclusive, }); defer target_fd.close(io); @@ -781,7 +841,7 @@ const Builder = struct { const source_stem = std.fs.path.basename(self.source.path)[5..]; // trim "meta." if (try targetExists(io, gpa, self.target_dir, source_stem, self.root_dir)) return; - const target_fd = try self.target_dir.createFile(io, source_stem, .{ + const target_fd = try self.target_dir.fd.createFile(io, source_stem, .{ .lock = .exclusive, }); defer target_fd.close(io); @@ -804,7 +864,7 @@ const Builder = struct { defer gpa.free(target_path); if (try targetExists(io, gpa, self.root_dir, target_path, self.root_dir)) return; - const target_fd = try self.root_dir.createFile(io, target_path, .{ + const target_fd = try self.root_dir.fd.createFile(io, target_path, .{ .lock = .exclusive, .read = true, .truncate = true, @@ -819,7 +879,7 @@ const Builder = struct { , .{ fd, target_path, self.source.path }); defer gpa.free(pandoc_command); - try shell(io, pandoc_command, .{ .cwd = .{ .dir = self.root_dir } }); + try shell(io, pandoc_command, .{ .cwd = .{ .dir = self.root_dir.fd } }); // track source try self.parts.append(gpa, try gpa.dupe(u8, self.source.path)); @@ -842,10 +902,16 @@ fn formatLink(gpa: Allocator, name: []const u8, pathPrefix: ?[]const u8) ![]u8 { return try std.fs.path.join(gpa, &.{ pathPrefix orelse "/", link_name }); } -fn targetExists(io: Io, gpa: Allocator, target_dir: Io.Dir, sub_path: []const u8, root_dir: Io.Dir) !bool { +fn targetExists( + io: Io, + gpa: Allocator, + target_dir: Dir, + sub_path: []const u8, + root_dir: Dir, +) !bool { if (FORCE) return false; - const target_fd = target_dir.openFile(io, sub_path, .{}) catch |err| + const target_fd = target_dir.fd.openFile(io, sub_path, .{}) catch |err| switch (err) { error.FileNotFound => return false, else => return err, @@ -1104,13 +1170,13 @@ test "e2e" { const Self = @This(); source: File, - root_dir: Io.Dir, + root_dir: Dir, expect_path: []const u8, expect_dgst: Digest, fn init(path: []const u8, expect_path: []const u8, expect_dgst: Digest) !Self { - const root_dir = try std.Io.Dir.cwd().openDir(io, std.fs.path.dirname(path).?, .{}); + const root_dir = try Dir.init(io, std.Io.Dir.cwd(), std.fs.path.dirname(path).?, .{}); return .{ .root_dir = root_dir, .source = try File.init(io, root_dir, std.fs.path.basename(path), null), -- cgit 1.4.1