Skip to main content

commit_wizard/engine/error/
code.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::engine::error::ErrorKind;
6
7pub mod exit_status {
8    // User input errors
9    pub const USER: i32 = 2;
10    // Configuration/state errors (10-19)
11    pub const CONFIG: i32 = 10;
12    pub const STATE: i32 = 11;
13    // External integration errors (20-29)
14    pub const REGISTRY: i32 = 20;
15    pub const GIT: i32 = 21;
16    pub const IO: i32 = 25;
17    pub const PROCESS: i32 = 26;
18    // Validation/workflow errors (30-39)
19    pub const VALIDATION: i32 = 30;
20    pub const RELEASE: i32 = 31;
21    pub const HOOK: i32 = 32;
22    pub const AI: i32 = 33;
23    // Runtime errors (50+)
24    pub const RUNTIME: i32 = 50;
25}
26
27macro_rules! error_codes {
28    (
29        $(
30            $variant:ident => ($kind:ident, $num:expr, $msg:expr)
31        ),* $(,)?
32    ) => {
33        #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34        pub enum ErrorCode {
35            $($variant),*
36        }
37
38        impl ErrorCode {
39            pub const fn kind(self) -> ErrorKind {
40                match self {
41                    $(Self::$variant => ErrorKind::$kind),*
42                }
43            }
44
45            pub const fn message(self) -> &'static str {
46                match self {
47                    $(Self::$variant => $msg),*
48                }
49            }
50
51            pub const fn index(self) -> u16 {
52                match self {
53                    $(Self::$variant => $num),*
54                }
55            }
56
57            pub const fn numeric(self) -> u16 {
58                self.kind().prefix() * 1000 + self.index()
59            }
60
61            pub fn id(self) -> String {
62                format!("E{:04}", self.numeric())
63            }
64
65            pub const fn exit_code(self) -> i32 {
66                match self.kind() {
67                    // User input errors
68                    ErrorKind::User => exit_status::USER,
69                    // Configuration/state errors (10-19)
70                    ErrorKind::Config => exit_status::CONFIG,
71                    ErrorKind::State => exit_status::STATE,
72                    // External integration errors (20-29)
73                    ErrorKind::Registry => exit_status::REGISTRY,
74                    ErrorKind::Git => exit_status::GIT,
75                    ErrorKind::Io => exit_status::IO,
76                    ErrorKind::Process => exit_status::PROCESS,
77                    // Validation/workflow errors (30-39)
78                    ErrorKind::Validation => exit_status::VALIDATION,
79                    ErrorKind::Release => exit_status::RELEASE,
80                    ErrorKind::Hook => exit_status::HOOK,
81                    ErrorKind::Ai => exit_status::AI,
82                    // Runtime errors (50+)
83                    ErrorKind::Runtime => exit_status::RUNTIME,
84                }
85            }
86        }
87
88        impl fmt::Display for ErrorCode {
89            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90                write!(f, "{} ({})", self.message(), self.id())
91            }
92        }
93    };
94}
95
96error_codes! {
97    UserCancelled => (User, 1, "Operation cancelled"),
98    UserInteractionRequired => (User, 2, "Interactive input required"),
99    UserInvalidArgument => (User, 3, "Invalid argument"),
100    FormatUnsupported => (User, 4, "Format is not supported for this command"),
101    InvalidInput => (User, 5, "Input is invalid"),
102
103    ConfigInvalid => (Config, 1, "Configuration is invalid"),
104    ConfigUnreadable => (Config, 2, "Configuration is unreadable"),
105    ConfigReferenceInvalid => (Config, 3, "Configuration reference is invalid"),
106
107    StateInvalid => (State, 1, "State is invalid"),
108    StateUnreadable => (State, 2, "State is unreadable"),
109
110    RegistryUnknown => (Registry, 1, "Registry is unknown"),
111    RegistryInvalid => (Registry, 2, "Registry is invalid"),
112    RegistryUnreachable => (Registry, 3, "Registry is unreachable"),
113    RegistrySyncFailed => (Registry, 4, "Registry sync failed"),
114    RegistryRefNotFound => (Registry, 5, "Registry ref not found"),
115    RegistrySectionMissing => (Registry, 6, "Registry section is missing"),
116    RegistryOffline => (Registry, 7, "Registry unavailable in offline mode"),
117
118    GitMissing => (Git, 1, "git is missing"),
119    GitRepoNotFound => (Git, 2, "Git repository not found"),
120    GitCommandFailed => (Git, 3, "Git command failed"),
121    GitPushBlocked => (Git, 4, "Push blocked by policy"),
122
123    ValidationFailed => (Validation, 1, "Validation failed"),
124    CommitInvalid => (Validation, 2, "Commit is invalid"),
125    PrInvalid => (Validation, 3, "Pull request is invalid"),
126    BranchInvalid => (Validation, 4, "Branch is invalid"),
127
128    ReleaseInvalid => (Release, 1, "Release is invalid"),
129    ReleaseTagExists => (Release, 2, "Release tag already exists"),
130    ReleaseBranchExists => (Release, 3, "Release branch already exists"),
131
132    HookInstallFailed => (Hook, 1, "Hook installation failed"),
133    HookExecutionFailed => (Hook, 2, "Hook execution failed"),
134
135    AiUnsupported => (Ai, 1, "AI is not supported for this command"),
136    AiProviderFailed => (Ai, 2, "AI provider failed"),
137
138    IoFailure => (Io, 1, "I/O failure"),
139    SerializationFailure => (Io, 2, "Serialization failure"),
140    UiPromptFailed => (Io, 3, "Prompt failed"),
141    OutputRenderFailure => (Io, 4, "Output render failure"),
142
143    ProcessFailure => (Process, 1, "Process failure"),
144
145    RuntimeFailure => (Runtime, 1, "Runtime failure"),
146}