1use std::error::Error;
8use std::fmt::{self, Display};
9
10pub type AmxResult<T> = Result<T, AmxError>;
12
13#[derive(Debug)]
18pub enum AmxError {
19 Exit = 1,
20 Assert = 2,
21 StackError = 3,
22 Bounds = 4,
23 MemoryAccess = 5,
24 InvalidInstruction = 6,
25 StackLow = 7,
26 HeapLow = 8,
27 Callback = 9,
28 Native = 10,
29 Divide = 11,
30 Sleep = 12,
31 InvalidState = 13,
32 Memory = 16,
33 Format = 17,
34 Version = 18,
35 NotFound = 19,
36 Index = 20,
37 Debug = 21,
38 Init = 22,
39 UserData = 23,
40 InitJit = 24,
41 Params = 25,
42 Domain = 26,
43 General = 27,
44 Overlay = 28,
45 Unknown,
46}
47
48impl Display for AmxError {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 #[allow(clippy::enum_glob_use)]
54 use self::AmxError::*;
55
56 match self {
57 Exit => write!(f, "Forced exit"),
58 Assert => write!(f, "Assertion failed"),
59 StackError => write!(f, "Stack / heap collision"),
60 Bounds => write!(f, "Index out of bounds"),
61 MemoryAccess => write!(f, "Invalid memory access"),
62 InvalidInstruction => write!(f, "Invalid instruction"),
63 StackLow => write!(f, "Stack underflow"),
64 HeapLow => write!(f, "Heap underflow"),
65 Callback => write!(f, "No callback or invalid callback"),
66 Native => write!(f, "Native function failed"),
67 Divide => write!(f, "Divide by zero"),
68 Sleep => write!(f, "Go into sleepmode"),
69 InvalidState => write!(f, "No implementation for this state, no fall-back"),
70 Memory => write!(f, "Out of memory"),
71 Format => write!(f, "Invalid file format"),
72 Version => write!(f, "File is for a newer version of AMX"),
73 NotFound => write!(f, "Function not found"),
74 Index => write!(f, "Invalid index parameter (bad entry point)"),
75 Debug => write!(f, "Debugger cannot run"),
76 Init => write!(f, "AMX not initialize"),
77 UserData => write!(f, "Unable to set user data field"),
78 InitJit => write!(f, "Cannot initialize the JIT"),
79 Params => write!(f, "Parameter error"),
80 Domain => write!(f, "Domain error, expression result does not fit in range"),
81 General => write!(f, "General error (unknown or unspecific error)"),
82 Overlay => write!(f, "Overlays are unsupported (JIT) or uninitialized"),
83 Unknown => write!(f, "Unknown error"),
84 }
85 }
86}
87
88impl Error for AmxError {}
89
90impl From<i32> for AmxError {
91 fn from(error_code: i32) -> Self {
92 match error_code {
93 1 => AmxError::Exit,
94 2 => AmxError::Assert,
95 3 => AmxError::StackError,
96 4 => AmxError::Bounds,
97 5 => AmxError::MemoryAccess,
98 6 => AmxError::InvalidInstruction,
99 7 => AmxError::StackLow,
100 8 => AmxError::HeapLow,
101 9 => AmxError::Callback,
102 10 => AmxError::Native,
103 11 => AmxError::Divide,
104 12 => AmxError::Sleep,
105 13 => AmxError::InvalidState,
106 16 => AmxError::Memory,
107 17 => AmxError::Format,
108 18 => AmxError::Version,
109 19 => AmxError::NotFound,
110 20 => AmxError::Index,
111 21 => AmxError::Debug,
112 22 => AmxError::Init,
113 23 => AmxError::UserData,
114 24 => AmxError::InitJit,
115 25 => AmxError::Params,
116 26 => AmxError::Domain,
117 27 => AmxError::General,
118 28 => AmxError::Overlay,
119 _ => AmxError::Unknown,
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn from_code_maps_all_known_errors() {
130 let cases: &[(i32, &str)] = &[
131 (1, "Exit"),
132 (2, "Assert"),
133 (3, "StackError"),
134 (4, "Bounds"),
135 (5, "MemoryAccess"),
136 (6, "InvalidInstruction"),
137 (7, "StackLow"),
138 (8, "HeapLow"),
139 (9, "Callback"),
140 (10, "Native"),
141 (11, "Divide"),
142 (12, "Sleep"),
143 (13, "InvalidState"),
144 (16, "Memory"),
145 (17, "Format"),
146 (18, "Version"),
147 (19, "NotFound"),
148 (20, "Index"),
149 (21, "Debug"),
150 (22, "Init"),
151 (23, "UserData"),
152 (24, "InitJit"),
153 (25, "Params"),
154 (26, "Domain"),
155 (27, "General"),
156 (28, "Overlay"),
157 ];
158
159 for &(code, expected_name) in cases {
160 let err = AmxError::from(code);
161 assert_eq!(
162 format!("{err:?}"),
163 expected_name,
164 "code {code} should map to {expected_name}"
165 );
166 }
167 }
168
169 #[test]
170 fn unknown_codes_map_to_unknown() {
171 for code in [0, 14, 15, 29, 100, -1, i32::MAX] {
172 assert!(
173 matches!(AmxError::from(code), AmxError::Unknown),
174 "code {code} should be Unknown"
175 );
176 }
177 }
178
179 #[test]
180 fn display_messages_are_not_empty() {
181 let errors = [
182 AmxError::Exit,
183 AmxError::Bounds,
184 AmxError::Divide,
185 AmxError::NotFound,
186 AmxError::Unknown,
187 ];
188
189 for err in errors {
190 let msg = format!("{err}");
191 assert!(!msg.is_empty(), "{err:?} has empty message");
192 }
193 }
194
195 #[test]
196 fn implements_std_error() {
197 let err = AmxError::General;
198 let _: &dyn std::error::Error = &err;
199 }
200
201 #[test]
202 fn memory_access_display() {
203 let err = AmxError::MemoryAccess;
204 assert_eq!(format!("{err}"), "Invalid memory access");
205 }
206
207 #[test]
208 fn memory_error_display() {
209 let err = AmxError::Memory;
210 assert_eq!(format!("{err}"), "Out of memory");
211 }
212
213 #[test]
214 fn amx_result_ok() {
215 let result: AmxResult<i32> = Ok(42);
216 assert!(result.is_ok());
217 }
218
219 #[test]
220 fn amx_result_err() {
221 let result: AmxResult<i32> = Err(AmxError::General);
222 assert!(result.is_err());
223 let err = AmxError::General;
224 assert_eq!(
225 format!("{err}"),
226 "General error (unknown or unspecific error)"
227 );
228 }
229}