// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie // SPDX-License-Identifier: MIT //! What a failed read of a repository was about, for a caller to show //! alongside the error: the object, the file, where in it, and a detail in //! git's own words where git has words for it. //! //! A function that can fail on the repository's account takes an optional //! `*Diagnostics` and fills in what it knows; fields it knows nothing //! about are left as they were, so a caller can reuse one across calls //! only if it resets it between them. Nothing here allocates, so filling //! one in cannot itself fail. const std = @import("std"); const Id = @import("object.zig").Id; const Diagnostics = @This(); /// The object being read, when the failure concerns one. object_id: ?Id = null, /// Where in the file, in bytes from its start, when that is known. offset: ?u64 = null, /// Where in the file, as a line counted from 1, when that is known. line: ?u32 = null, path_buffer: [max_path_len]u8 = undefined, path_len: usize = 0, detail_buffer: [max_detail_len]u8 = undefined, detail_len: usize = 0, /// The longest path kept; a longer one is cut short. pub const max_path_len = 4096; /// The longest detail kept; a longer one is cut short. pub const max_detail_len = 2048; /// Empties every field. pub fn reset(d: *Diagnostics) void { d.* = .{}; } /// The file being read, as the caller named it, or null if there was none. pub fn path(d: *const Diagnostics) ?[]const u8 { if (d.path_len == 0) return null; return d.path_buffer[0..d.path_len]; } /// Records the file being read: `parts` joined with `/`. pub fn setPath(d: *Diagnostics, parts: []const []const u8) void { var w: std.Io.Writer = .fixed(&d.path_buffer); for (parts, 0..) |part, i| { if (i != 0) w.writeByte('/') catch break; w.writeAll(part) catch { w.end = w.buffer.len; break; }; } d.path_len = w.end; } /// More about what went wrong, or an empty string. pub fn detail(d: *const Diagnostics) []const u8 { return d.detail_buffer[0..d.detail_len]; } /// Records more about what went wrong, formatted. pub fn setDetail(d: *Diagnostics, comptime fmt: []const u8, args: anytype) void { var w: std.Io.Writer = .fixed(&d.detail_buffer); w.print(fmt, args) catch { w.end = w.buffer.len; }; d.detail_len = w.end; } test "a diagnostic keeps what it was told, cut short if need be" { var d: Diagnostics = .{}; try std.testing.expectEqual(null, d.path()); d.setPath(&.{ ".git/objects", "ab", "cdef" }); try std.testing.expectEqualStrings(".git/objects/ab/cdef", d.path().?); var long: [max_detail_len + 10]u8 = @splat('x'); d.setDetail("{s}", .{&long}); try std.testing.expectEqual(max_detail_len, d.detail().len); d.reset(); try std.testing.expectEqualStrings("", d.detail()); }