I’m here to promote my diagnostic pattern again…
In this pattern, you push an InvalidCharacter first, then throw a ParseIntError . This manually maintained error stack records the source of each error, allowing errors thrown independently at higher levels to be functionally considered the error set of the lower level.
fn parse_csv(last_diagnostic: *Diagnostic) !void {
// now in the middle of this mess we have a int parsing error.
try last_diagnostic.enterStack(error.InvalidCharacter);
return error.ParseIntError;
}
fn baz(last_diagnostic: *Diagnostic) !void {
parse_csv(last_diagnostic) catch |err| {
try last_diagnostic.enterStack(err);
return error.ParseCsvFailed;
};
}
test "new diagnostics" {
const root_allocator = std.testing.allocator;
var arena = std.heap.ArenaAllocator.init(root_allocator);
defer arena.deinit();
var diagnostics: Diagnostics = .{ .arena = arena };
baz(&diagnostics.last_diagnostic) catch |err| {
diagnostics.log_all(err);
diagnostics.clear();
};
}