const std = @import("std"); const Allocator = std.mem.Allocator; const Error = @import("./error.zig").Error; const Trap = @import("./trap.zig").Trap; const TrapCode = @import("./trap.zig").TrapCode; const TrapError = @import("./trap.zig").TrapError; pub const ExitStatus = c_int; pub const ErrorUnion = union(enum) { none: void, err: ?ExitStatus, trap: ?TrapCode, }; pub const Diagnostics = struct { allocator: Allocator, debug_print: bool, data: ErrorUnion = .{ .none = {} }, message: ?[]const u8 = null, pub fn init(allocator: Allocator, debug_print: bool) !*Diagnostics { var result = try allocator.create(Diagnostics); result.* = .{ .allocator = allocator, .debug_print = debug_print }; return result; } pub fn clear(self: *Diagnostics) void { if (self.message) |msg| self.allocator.free(msg); self.data = .{ .none = {} }; self.message = null; } pub fn deinit(self: *Diagnostics) void { if (self.message) |msg| self.allocator.free(msg); self.allocator.destroy(self); } pub fn handleError( err: ?*Error, err_enum: anyerror, result: anytype, diag: ?*Diagnostics, ) @TypeOf(err_enum)!@TypeOf(result) { if (err) |e| { defer e.deinit(); if (diag) |d| { var msg = e.getMessage(); defer msg.deinit(); // If the message ends with NUL byte, don't include it. const size = if (msg.data[msg.size - 1] == 0) msg.size - 1 else msg.size; d.data = .{ .err = e.getExitStatus() }; d.message = try d.allocator.alloc(u8, size); @memcpy(@constCast(d.message.?), msg.data); if (d.debug_print) std.debug.print("{s}\n", .{d.message.?}); } return err_enum; } else { if (diag) |d| d.clear(); return result; } } pub fn handleTrap( trap: ?*Trap, result: anytype, diag: ?*Diagnostics, ) !@TypeOf(result) { if (trap) |t| { defer t.deinit(); const code = t.getTrapCode(); if (diag) |d| { var msg = t.getMessage(); defer msg.deinit(); // If the message ends with NUL byte, don't include it. const size = if (msg.data[msg.size - 1] == 0) msg.size - 1 else msg.size; d.data = .{ .trap = code }; d.message = try d.allocator.alloc(u8, size); @memcpy(@constCast(d.message.?), msg.data); if (d.debug_print) std.debug.print("{s}\n", .{d.message.?}); } return if (code) |c| c.toError() else TrapError.UnknownTrap; } else { if (diag) |d| d.clear(); return result; } } pub fn handleErrorOrTrap( err: ?*Error, err_enum: anyerror, trap: ?*Trap, result: anytype, diag: ?*Diagnostics, ) !@TypeOf(result) { try handleError(err, err_enum, {}, diag); return try handleTrap(trap, result, diag); } };