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 17 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}