diff options
| author | Robert Günzler <r@gnzler.io> | 2026-06-24 11:57:02 +0900 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2026-09-22 18:01:30 +0200 |
| commit | c677e8873e0eae790a1b8b07844a10efa9f63a1e (patch) | |
| tree | 95bc9709c374d27962a73712d24807a98cc69bb4 /src/fs.zig | |
| parent | fcdf352af595759aa34e6ed037ee10810867a616 (diff) | |
| download | site-c677e8873e0eae790a1b8b07844a10efa9f63a1e.tar.gz | |
wip design for v2
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'src/fs.zig')
| -rw-r--r-- | src/fs.zig | 239 |
1 files changed, 0 insertions, 239 deletions
diff --git a/src/fs.zig b/src/fs.zig deleted file mode 100644 index f139653..0000000 --- a/src/fs.zig +++ /dev/null @@ -1,239 +0,0 @@ -const std = @import("std"); -const c = @cImport({ - @cInclude("stdlib.h"); - @cInclude("time.h"); -}); - -const exe = @import("exe.zig"); - -const log = std.log.scoped(.fs); - -pub const FileSwapper = struct { - allocator: std.mem.Allocator, - target_path: []const u8, - tmp_path: []const u8, - tmp: std.fs.File, - swapped: *bool, - - 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, .{}); - - const swapped = try alloc.create(bool); - swapped.* = false; - - return .{ - .allocator = alloc, - .target_path = path, - .tmp_path = tmp_path, - .tmp = f, - .swapped = swapped, - }; - } - - pub fn file(self: FileSwapper) std.fs.File { - return self.tmp; - } - - pub fn writer(self: FileSwapper, buffer: []u8) std.fs.File.Writer { - return self.tmp.writer(buffer); - } - - pub fn swap(self: FileSwapper) !void { - std.log.debug("swapping {s} -> {s}", .{ self.tmp_path, self.target_path }); - - // flush writes - self.tmp.sync() catch - @panic("sync tmp file"); - - // swap file into final location - // NOTE: the tmp fd is still valid after this - try std.fs.rename( - std.fs.cwd(), - self.tmp_path, - std.fs.cwd(), - self.target_path, - ); - - self.swapped.* = true; - } - - pub fn close(self: FileSwapper) void { - var found = true; - std.fs.cwd().access(self.tmp_path, .{}) catch |e| switch (e) { - error.FileNotFound => found = false, - else => @panic("access tmp path"), - }; - - defer self.allocator.free(self.tmp_path); - defer self.allocator.destroy(self.swapped); - defer self.tmp.close(); - - if (found) { - const changed = diff(self.tmp_path, self.target_path) catch - @panic("failed to diff tmp and target file"); - - if (changed) { - self.swap() catch - @panic("failed to swap target file"); - } else { - log.debug("{s} no changes, skipping", .{ self.target_path }); - std.posix.unlink(self.tmp_path) catch - @panic("failed to cleanup tmp file"); - } - } - } -}; - -pub fn replaceExtension(alloc: std.mem.Allocator, p: []const u8, new: []const u8) ![]const u8 { - const dirname = std.fs.path.dirname(p) orelse "."; - const basename = try stripExtension(std.fs.path.basename(p)); - return try joinPathAndFilename( - alloc, - &.{dirname}, - &.{ basename, ".", new }, - ); -} - -// also trims off base paths -pub fn stripExtension(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 joinPathAndFilename(alloc: std.mem.Allocator, paths: []const []const u8, filename_parts: []const []const u8) ![]const u8 { - var buf: [std.os.linux.PATH_MAX]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&buf); - - const filename = try std.mem.concat(fba.allocator(), u8, filename_parts); - - var p = try std.ArrayList([]const u8).initCapacity(fba.allocator(), paths.len + 1); - p.appendSliceAssumeCapacity(paths); - p.appendAssumeCapacity(filename); - - std.debug.assert(paths.len + 1 == p.items.len); - return try std.fs.path.join(alloc, p.items); -} - -// pub fn getGitTime(path: []const u8) !std.posix.timespec { -// var buf = std.mem.zeroes([12]u8); -// -// var p = std.process.Child.init( -// &.{ "git", "log", "-1", "--pretty=format:%at", "-z", "--", path }, -// std.heap.page_allocator, -// ); -// p.stdout_behavior = .Pipe; -// try p.spawn(); -// _ = try p.stdout.?.readAll(&buf); -// const result = try p.wait(); -// -// std.debug.assert(result == .Exited and result.Exited == 0); -// -// // parse result as ulong -// const gitAuthorDate: c_ulong = c.strtoul(&buf, null, 10); -// if (gitAuthorDate == 0) return error.NoGitTime; -// -// // calculate timespec -// return .{ -// .sec = std.math.cast(isize, gitAuthorDate) orelse std.math.maxInt(isize), -// .nsec = 0, -// }; -// } - -pub fn getMtime(path: []const u8) !std.posix.timespec { - const fd = try std.fs.cwd().openFile(path, .{ .mode = .read_only }); - defer fd.close(); - - const meta = try fd.stat(); - const mtime = meta.mtime; - - return .{ - .sec = std.math.cast(isize, @divFloor(mtime, std.time.ns_per_s)) orelse std.math.maxInt(isize), - .nsec = std.math.cast(isize, @mod(mtime, std.time.ns_per_s)) orelse std.math.maxInt(isize), - }; -} - -pub fn getLastEdit(path: []const u8) !std.posix.timespec { - std.fs.cwd().access(path, .{}) catch |e| switch (e) { - error.FileNotFound => return .{ - .sec = 0, - .nsec = 0, - }, - else => return e, - }; - return getMtime(path); -} - -pub fn setMtime(path: []const u8, ts: std.posix.timespec) !void { - const fd = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); - defer fd.close(); - - try std.posix.futimens( - fd.handle, - &[2]std.os.linux.timespec{ ts, ts }, - ); -} - -// use libc strftime to format time string -pub fn formatTimespec(alloc: std.mem.Allocator, timespec: std.posix.timespec, fmt: [*c]const u8) ![]const u8 { - const mtime_str = try alloc.alloc(u8, std.os.linux.PATH_MAX); - var tm: c.struct_tm = undefined; - _ = c.localtime_r(×pec.sec, &tm); - const rc = c.strftime( - mtime_str.ptr, - mtime_str.len, - fmt, - &tm, - ); - switch (std.posix.errno(rc)) { - .SUCCESS => {}, - else => |err| return std.posix.unexpectedErrno(err), - } - return try alloc.realloc(mtime_str, rc); -} - -pub fn diff(a: []const u8, b: []const u8) !bool { - std.fs.cwd().access(a, .{}) catch |e| switch (e) { - error.FileNotFound => return true, - else => return e, - }; - std.fs.cwd().access(b, .{}) catch |e| switch (e) { - error.FileNotFound => return true, - else => return e, - }; - - var p = std.process.Child.init(&.{ - "diff", - "-q", - a, - b, - }, std.heap.page_allocator); - p.stdin_behavior = .Close; - p.stderr_behavior = .Ignore; - p.stdout_behavior = .Ignore; - try p.spawn(); - switch (try p.wait()) { - .Exited => |exit_code| switch (exit_code) { - 0 => return false, - 1 => return true, - else => return error.UnknownDiffError, - }, - else => return error.UnknownDiffError, - } -} |