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