From 69ea02e682fa63910cc8316e8b55a3aa60643e83 Mon Sep 17 00:00:00 2001 From: Robert Günzler Date: Fri, 25 Jul 2025 19:20:52 +0200 Subject: refactor gen binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - make indexer also pick up markdown files - prepare for hosting more markdown functionality Signed-off-by: Robert Günzler --- src/fs.zig | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/fs.zig (limited to 'src/fs.zig') diff --git a/src/fs.zig b/src/fs.zig new file mode 100644 index 0000000..c009afd --- /dev/null +++ b/src/fs.zig @@ -0,0 +1,116 @@ +const std = @import("std"); +const c = @cImport({ + @cInclude("time.h"); +}); + +pub const FileSwapper = struct { + allocator: std.mem.Allocator, + target_path: []const u8, + tmp_path: []const u8, + tmp: std.fs.File, + + pub fn createAt(alloc: std.mem.Allocator, path: []const u8) !FileSwapper { + const target_dir = std.fs.path.dirname(path) orelse "."; + const target_name = std.fs.path.basename(path); + + const tmp_name = try std.fmt.allocPrint( + alloc, + ".{s}", + .{target_name}, + ); + defer alloc.free(tmp_name); + + const tmp_path = try std.fs.path.join( + alloc, + &.{ target_dir, tmp_name }, + ); + + const f = try std.fs.cwd().createFile(tmp_path, .{}); + + return .{ + .allocator = alloc, + .target_path = path, + .tmp_path = tmp_path, + .tmp = f, + }; + } + + pub fn writer(self: FileSwapper) std.fs.File.Writer { + return self.tmp.writer(); + } + + pub fn close(self: FileSwapper) void { + defer self.allocator.free(self.tmp_path); + defer self.tmp.close(); + + // flush writes + self.tmp.sync() catch + @panic("sync tmp file"); + + // swap file into final location + std.fs.rename( + std.fs.cwd(), + self.tmp_path, + std.fs.cwd(), + self.target_path, + ) catch + @panic("rename tmp file"); + } +}; + +pub fn replace_extension(alloc: std.mem.Allocator, p: []const u8, new: []const u8) ![]const u8 { + const dirname = std.fs.path.dirname(p) orelse "."; + const basename = try strip_extension(p); + return try join_path_and_filename( + alloc, + &.{dirname}, + &.{ basename, ".", new }, + ); +} + +// also trims off base paths +pub fn strip_extension(path: []const u8) ![]const u8 { + const ext = std.fs.path.extension(path); + const ext_idx = std.mem.indexOfPosLinear(u8, path, 0, ext); + std.debug.assert(ext_idx != null); + return path[0..ext_idx.?]; +} + +pub fn join_path_and_filename(alloc: std.mem.Allocator, paths: []const []const u8, filename_parts: []const []const u8) ![]const u8 { + const filename = try std.mem.concat(alloc, u8, filename_parts); + defer alloc.free(filename); + + var p = try std.ArrayList([]const u8).initCapacity(alloc, paths.len + 1); + defer p.deinit(); + + p.appendSliceAssumeCapacity(paths); + p.appendAssumeCapacity(filename); + + const paths2 = try p.toOwnedSlice(); + defer alloc.free(paths2); + + std.debug.assert(paths.len + 1 == paths2.len); + return try std.fs.path.join(alloc, paths2); +} + +pub fn get_mtime(alloc: std.mem.Allocator, path: []const u8) ![]const u8 { + const attr = try alloc.create(std.os.linux.Stat); + defer alloc.destroy(attr); + + const pathZ = try alloc.dupeZ(u8, path); + defer alloc.free(pathZ); + + // use stat to get mtime + _ = std.os.linux.stat(pathZ, attr); + + // use libc strftime to format time string + const mtime = attr.mtime(); + const mtime_str = try alloc.alloc(u8, 512); + const n = c.strftime( + mtime_str.ptr, + mtime_str.len, + "%F", + c.localtime(&mtime.sec), + ); + return try alloc.realloc(mtime_str, n); +} -- cgit 1.4.1