From 20ce7279d2fdd366112e3877dd57495fd261b938 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Mon, 21 Apr 2025 18:12:59 +0200 Subject: [PATCH] feat: add git.workspace_path function And refactor the git module. --- src/git.zig | 105 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 17 deletions(-) diff --git a/src/git.zig b/src/git.zig index b2b4626..80413ae 100644 --- a/src/git.zig +++ b/src/git.zig @@ -3,30 +3,101 @@ const tp = @import("thespian"); const shell = @import("shell"); const bin_path = @import("bin_path"); +pub const Error = error{ OutOfMemory, GitNotFound, GitCallFailed }; + +const log_execute = false; + +pub fn workspace_path() Error!void { + const fn_name = @src().fn_name; + try git(.{ "rev-parse", "--show-toplevel" }, struct { + fn result(parent: tp.pid_ref, output: []const u8) void { + var it = std.mem.splitScalar(u8, output, '\n'); + while (it.next()) |branch| if (branch.len > 0) + parent.send(.{ module_name, fn_name, branch }) catch {}; + } + }.result, exit_null_on_error(fn_name)); +} + +pub fn current_branch() Error!void { + const fn_name = @src().fn_name; + try git(.{ "rev-parse", "--abbrev-ref", "HEAD" }, struct { + fn result(parent: tp.pid_ref, output: []const u8) void { + var it = std.mem.splitScalar(u8, output, '\n'); + while (it.next()) |branch| if (branch.len > 0) + parent.send(.{ module_name, fn_name, branch }) catch {}; + } + }.result, exit_null_on_error(fn_name)); +} + +fn git( + cmd: anytype, + out: OutputHandler, + exit: ExitHandler, +) Error!void { + return git_err(cmd, out, noop, exit); +} + +fn git_err( + cmd: anytype, + out: OutputHandler, + err: OutputHandler, + exit: ExitHandler, +) Error!void { + const cbor = @import("cbor"); + const git_binary = get_git() orelse return error.GitNotFound; + var buf: std.ArrayListUnmanaged(u8) = .empty; + const writer = buf.writer(allocator); + switch (@typeInfo(@TypeOf(cmd))) { + .@"struct" => |info| if (info.is_tuple) { + try cbor.writeArrayHeader(writer, info.fields.len + 1); + try cbor.writeValue(writer, git_binary); + inline for (info.fields) |f| + try cbor.writeValue(writer, @field(cmd, f.name)); + return shell.execute(allocator, .{ .buf = buf.items }, .{ + .out = to_shell_output_handler(out), + .err = to_shell_output_handler(err), + .exit = exit, + .log_execute = log_execute, + }) catch error.GitCallFailed; + }, + else => {}, + } + @compileError("git command should be a tuple: " ++ @typeName(@TypeOf(cmd))); +} + +fn exit_null_on_error(comptime tag: []const u8) shell.ExitHandler { + return struct { + fn exit(_: usize, parent: tp.pid_ref, _: []const u8, _: []const u8, exit_code: i64) void { + if (exit_code > 0) + parent.send(.{ module_name, tag, null }) catch {}; + } + }.exit; +} + +const OutputHandler = fn (parent: tp.pid_ref, output: []const u8) void; +const ExitHandler = shell.ExitHandler; + +fn to_shell_output_handler(handler: anytype) shell.OutputHandler { + return struct { + fn out(_: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void { + handler(parent, output); + } + }.out; +} + +fn noop(_: tp.pid_ref, _: []const u8) void {} + var git_path: ?struct { path: ?[:0]const u8 = null, } = null; +const allocator = std.heap.c_allocator; + fn get_git() ?[]const u8 { if (git_path) |p| return p.path; - const path = bin_path.find_binary_in_path(std.heap.c_allocator, "git") catch null; + const path = bin_path.find_binary_in_path(allocator, module_name) catch null; git_path = .{ .path = path }; return path; } -pub fn get_current_branch(allocator: std.mem.Allocator) !void { - const git_binary = get_git() orelse return error.GitBinaryNotFound; - const git_current_branch_cmd = tp.message.fmt(.{ git_binary, "rev-parse", "--abbrev-ref", "HEAD" }); - const handlers = struct { - fn out(_: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void { - var it = std.mem.splitScalar(u8, output, '\n'); - while (it.next()) |branch| if (branch.len > 0) - parent.send(.{ "git", "current_branch", branch }) catch {}; - } - }; - try shell.execute(allocator, git_current_branch_cmd, .{ - .out = handlers.out, - .err = shell.log_err_handler, - .exit = shell.log_exit_err_handler, - }); -} +const module_name = @typeName(@This());