1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(i32)]
14pub enum ExitCode {
15 Success = 0,
17
18 GeneralError = 1,
20
21 UsageError = 2,
23
24 NetworkError = 3,
26
27 AuthError = 4,
29
30 NotFound = 5,
32
33 Conflict = 6,
35
36 UnsupportedFeature = 7,
38
39 Interrupted = 130,
41}
42
43impl ExitCode {
44 #[inline]
46 pub const fn as_i32(self) -> i32 {
47 self as i32
48 }
49
50 pub const fn from_i32(code: i32) -> Option<Self> {
54 match code {
55 0 => Some(Self::Success),
56 1 => Some(Self::GeneralError),
57 2 => Some(Self::UsageError),
58 3 => Some(Self::NetworkError),
59 4 => Some(Self::AuthError),
60 5 => Some(Self::NotFound),
61 6 => Some(Self::Conflict),
62 7 => Some(Self::UnsupportedFeature),
63 130 => Some(Self::Interrupted),
64 _ => None,
65 }
66 }
67
68 pub const fn description(self) -> &'static str {
70 match self {
71 Self::Success => "Operation completed successfully",
72 Self::GeneralError => "General error",
73 Self::UsageError => "Invalid arguments or path format",
74 Self::NetworkError => "Network error (retryable)",
75 Self::AuthError => "Authentication or permission failure",
76 Self::NotFound => "Resource not found",
77 Self::Conflict => "Conflict or precondition failure",
78 Self::UnsupportedFeature => "Feature not supported by backend",
79 Self::Interrupted => "Operation interrupted",
80 }
81 }
82}
83
84impl From<ExitCode> for i32 {
85 fn from(code: ExitCode) -> Self {
86 code.as_i32()
87 }
88}
89
90impl std::fmt::Display for ExitCode {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 write!(f, "{} ({})", self.description(), self.as_i32())
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_exit_code_values() {
102 assert_eq!(ExitCode::Success.as_i32(), 0);
103 assert_eq!(ExitCode::GeneralError.as_i32(), 1);
104 assert_eq!(ExitCode::UsageError.as_i32(), 2);
105 assert_eq!(ExitCode::NetworkError.as_i32(), 3);
106 assert_eq!(ExitCode::AuthError.as_i32(), 4);
107 assert_eq!(ExitCode::NotFound.as_i32(), 5);
108 assert_eq!(ExitCode::Conflict.as_i32(), 6);
109 assert_eq!(ExitCode::UnsupportedFeature.as_i32(), 7);
110 assert_eq!(ExitCode::Interrupted.as_i32(), 130);
111 }
112
113 #[test]
114 fn test_exit_code_from_i32() {
115 assert_eq!(ExitCode::from_i32(0), Some(ExitCode::Success));
116 assert_eq!(ExitCode::from_i32(1), Some(ExitCode::GeneralError));
117 assert_eq!(ExitCode::from_i32(2), Some(ExitCode::UsageError));
118 assert_eq!(ExitCode::from_i32(3), Some(ExitCode::NetworkError));
119 assert_eq!(ExitCode::from_i32(4), Some(ExitCode::AuthError));
120 assert_eq!(ExitCode::from_i32(5), Some(ExitCode::NotFound));
121 assert_eq!(ExitCode::from_i32(6), Some(ExitCode::Conflict));
122 assert_eq!(ExitCode::from_i32(7), Some(ExitCode::UnsupportedFeature));
123 assert_eq!(ExitCode::from_i32(130), Some(ExitCode::Interrupted));
124 assert_eq!(ExitCode::from_i32(99), None);
125 }
126
127 #[test]
128 fn test_exit_code_into_i32() {
129 let code: i32 = ExitCode::Success.into();
130 assert_eq!(code, 0);
131
132 let code: i32 = ExitCode::NotFound.into();
133 assert_eq!(code, 5);
134 }
135
136 #[test]
137 fn test_exit_code_display() {
138 let display = format!("{}", ExitCode::Success);
139 assert!(display.contains("0"));
140 assert!(display.contains("successfully"));
141
142 let display = format!("{}", ExitCode::NotFound);
143 assert!(display.contains("5"));
144 assert!(display.contains("not found"));
145 }
146}