errors.zig

4.4 KB · raw

1const std = @import("std");23pub const NinaError = error{4    PackageNotFound,5    MachineNotFound,6    GenerationNotFound,7    ConfigNotFound,8    ConfigParseError,9    BuildFailed,10    SshFailed,11    SearchFailed,12    NetworkError,13    OutOfMemory,14    ProcessFailed,15    Internal,16    // common IO errors that bubble up17    FileNotFound,18    AccessDenied,19    SystemResources,20    Unexpected,21};2223pub var error_info: ErrorInfo = .{};2425pub const ErrorInfo = struct {26    message: [512]u8 = undefined,27    message_len: usize = 0,28    detail: [512]u8 = undefined,29    detail_len: usize = 0,30    suggestion: [256]u8 = undefined,31    suggestion_len: usize = 0,3233    pub fn setMessage(self: *ErrorInfo, comptime fmt: []const u8, args: anytype) void {34        const r = std.fmt.bufPrint(&self.message, fmt, args) catch return;35        self.message_len = r.len;36    }3738    pub fn setDetail(self: *ErrorInfo, comptime fmt: []const u8, args: anytype) void {39        const r = std.fmt.bufPrint(&self.detail, fmt, args) catch return;40        self.detail_len = r.len;41    }4243    pub fn setSuggestion(self: *ErrorInfo, comptime fmt: []const u8, args: anytype) void {44        const r = std.fmt.bufPrint(&self.suggestion, fmt, args) catch return;45        self.suggestion_len = r.len;46    }4748    pub fn getMessage(self: *const ErrorInfo) ?[]const u8 {49        if (self.message_len == 0) return null;50        return self.message[0..self.message_len];51    }5253    pub fn getDetail(self: *const ErrorInfo) ?[]const u8 {54        if (self.detail_len == 0) return null;55        return self.detail[0..self.detail_len];56    }5758    pub fn getSuggestion(self: *const ErrorInfo) ?[]const u8 {59        if (self.suggestion_len == 0) return null;60        return self.suggestion[0..self.suggestion_len];61    }6263    pub fn reset(self: *ErrorInfo) void {64        self.message_len = 0;65        self.detail_len = 0;66        self.suggestion_len = 0;67    }68};6970const BuildPattern = struct { pattern: []const u8, message: []const u8, suggestion: ?[]const u8 = null };71const build_patterns = [_]BuildPattern{72    .{ .pattern = "undefined variable", .message = "package not found in nixpkgs", .suggestion = "nina search <name>" },73    .{ .pattern = "has been renamed to", .message = "package was renamed — update your config" },74    .{ .pattern = "attribute '", .message = "package doesn't exist in this nixpkgs version" },75    .{ .pattern = "collision between", .message = "two packages are claiming the same file — remove one" },76    .{ .pattern = "infinite recursion", .message = "circular reference in configuration.nix — check recent edits" },77    .{ .pattern = "No space left on device", .message = "disk is full", .suggestion = "nina clean" },78    .{ .pattern = "hash mismatch", .message = "download checksum mismatch — try again" },79    .{ .pattern = "cannot write lock file", .message = "can't write flake.lock", .suggestion = "use --no-write-lock-file" },80    .{ .pattern = "SSL certificate", .message = "ssl error — check your network" },81    .{ .pattern = "permission denied", .message = "permission denied — try with sudo" },82};8384pub fn translateBuildError(stderr: []const u8) struct { message: []const u8, suggestion: ?[]const u8 } {85    for (build_patterns) |p| {86        if (std.mem.indexOf(u8, stderr, p.pattern) != null) {87            return .{ .message = p.message, .suggestion = p.suggestion };88        }89    }90    return .{ .message = stderr, .suggestion = null };91}9293pub fn staticMessage(err: anyerror) []const u8 {94    return switch (err) {95        error.ConfigNotFound => "~/.nina.conf not found",96        error.ConfigParseError => "could not parse ~/.nina.conf",97        error.SearchFailed => "nix search failed",98        error.NetworkError => "network unavailable",99        error.OutOfMemory => "out of memory",100        error.ProcessFailed => "process failed",101        error.SshFailed => "ssh connection failed",102        error.MachineNotFound => "machine not found in config",103        error.PackageNotFound => "package not found",104        error.GenerationNotFound => "generation not found",105        error.BuildFailed => "build failed",106        else => "internal error",107    };108}109110test "build error translation" {111    const r = translateBuildError("error: undefined variable 'pkgs.notexist'");112    try std.testing.expectEqualStrings("package not found in nixpkgs", r.message);113}114115test "static message" {116    try std.testing.expectEqualStrings("~/.nina.conf not found", staticMessage(error.ConfigNotFound));117}