From 51ddaaf3917ad7968df877d10580d1eadb926688 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 05:04:57 -0300 Subject: Restore and ThemeFile failures say why in words, a ZON one with its line: never FileTooLarge, ParseZon or a bare `parse zon` Restore read failures named the Zig error (FileTooLarge, NotAFile, ReadFailed), a file that is no dump said `not a pardes dump (ParseZon)`, and a ThemeFile that did not parse said `parse zon`. Messages.errorWords says an error in words (the few that read badly spelled out plainly, the rest spaced and lower case, as reportError did), and zonWhy gives the parser's first error with its line: `not a pardes dump: line 1: expected ...`, `theme file x.zon: line 3: ...`. Co-Authored-By: Claude Opus 5.5 --- src/Messages.zig | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'src/Messages.zig') diff --git a/src/Messages.zig b/src/Messages.zig index a316377a..366445c7 100644 --- a/src/Messages.zig +++ b/src/Messages.zig @@ -486,13 +486,53 @@ pub fn reportError(p: *Pardes, id: usize, operation: []const u8, err: anyerror) p.fs.no_pane_slot_len = @intCast(w.buffered().len); return reportFailure(p, id, w.buffered()); } - for (@errorName(err), 0..) |c, i| { - if (std.ascii.isUpper(c) and i > 0) w.writeByte(' ') catch break; - w.writeByte(std.ascii.toLower(c)) catch break; - } + var words: [128]u8 = undefined; + w.writeAll(errorWords(err, &words)) catch {}; reportFailure(p, id, w.buffered()); } +/// An error in a person's words, never its Zig name: the few that read +/// badly spaced out said plainly, the rest spaced and lower case +/// (`FileNotFound`, "file not found"). +pub fn errorWords(err: anyerror, buf: []u8) []const u8 { + return switch (err) { + error.FileTooLarge => "too large", + error.NotAFile => "not a regular file", + error.ReadFailed => "could not be read", + error.ParseZon => "not valid ZON", + error.ThemeFileTooLarge => "too large: a theme file is at most 1 MiB", + error.InvalidThemeName => "its name is empty or over 255 bytes", + else => { + var w = std.Io.Writer.fixed(buf); + for (@errorName(err), 0..) |c, i| { + if (std.ascii.isUpper(c) and i > 0) w.writeByte(' ') catch break; + w.writeByte(std.ascii.toLower(c)) catch break; + } + return w.buffered(); + }, + }; +} + +/// A ZON parse failure in a person's words: the first error the parser +/// found and its line, `line 3: expected ','`. +pub fn zonWhy(diag: *const std.zon.parse.Diagnostics, buf: []u8) []const u8 { + var errors = diag.iterateErrors(); + const first = errors.next() orelse return "not valid ZON"; + const loc = first.getLocation(diag); + return std.fmt.bufPrint(buf, "line {d}: {f}", .{ loc.line + 1, first.fmtMessage(diag) }) catch "not valid ZON"; +} + +test "errors are said in words, and a ZON failure names its line" { + var buf: [128]u8 = undefined; + try std.testing.expectEqualStrings("file not found", errorWords(error.FileNotFound, &buf)); + try std.testing.expectEqualStrings("too large", errorWords(error.FileTooLarge, &buf)); + var diag: std.zon.parse.Diagnostics = .{}; + defer diag.deinit(std.testing.allocator); + const T = struct { a: u8 }; + try std.testing.expectError(error.ParseZon, std.zon.parse.fromSliceAlloc(T, std.testing.allocator, ".{\n .a = 1,\n .b = 2 }", &diag, .{})); + try std.testing.expect(std.mem.startsWith(u8, zonWhy(&diag, &buf), "line 3: ")); +} + /// The longest start of `text` that fits `max` bytes and ends between /// words (or, with no space to cut at, between characters). pub fn clip(text: []const u8, max: usize) []const u8 { -- cgit v1.3