/home/noah/src/realizar/src/error.rs
Line | Count | Source |
1 | | //! Error types for Realizar |
2 | | //! |
3 | | //! This module defines all error types used throughout the library. |
4 | | |
5 | | use thiserror::Error; |
6 | | |
7 | | /// Result type alias for Realizar operations |
8 | | pub type Result<T> = std::result::Result<T, RealizarError>; |
9 | | |
10 | | /// Error type for all Realizar operations |
11 | | #[derive(Error, Debug, Clone, PartialEq)] |
12 | | pub enum RealizarError { |
13 | | /// Shape mismatch between tensors |
14 | | #[error("Shape mismatch: expected {expected:?}, got {actual:?}")] |
15 | | ShapeMismatch { |
16 | | /// Expected shape |
17 | | expected: Vec<usize>, |
18 | | /// Actual shape |
19 | | actual: Vec<usize>, |
20 | | }, |
21 | | |
22 | | /// Invalid shape specification |
23 | | #[error("Invalid shape: {reason}")] |
24 | | InvalidShape { |
25 | | /// Reason for invalidity |
26 | | reason: String, |
27 | | }, |
28 | | |
29 | | /// Data size does not match shape |
30 | | #[error("Data size {data_size} does not match shape {shape:?} (expected {expected})")] |
31 | | DataShapeMismatch { |
32 | | /// Actual data size |
33 | | data_size: usize, |
34 | | /// Specified shape |
35 | | shape: Vec<usize>, |
36 | | /// Expected size from shape |
37 | | expected: usize, |
38 | | }, |
39 | | |
40 | | /// Invalid dimension for operation |
41 | | #[error("Invalid dimension {dim} for tensor with {ndim} dimensions")] |
42 | | InvalidDimension { |
43 | | /// Requested dimension |
44 | | dim: usize, |
45 | | /// Number of dimensions |
46 | | ndim: usize, |
47 | | }, |
48 | | |
49 | | /// Matrix multiplication dimension mismatch |
50 | | #[error("Matrix multiplication dimension mismatch: ({m}×{k}) × ({k2}×{n})")] |
51 | | MatmulDimensionMismatch { |
52 | | /// Rows in first matrix |
53 | | m: usize, |
54 | | /// Columns in first matrix / rows in second matrix |
55 | | k: usize, |
56 | | /// Rows in second matrix (should equal k) |
57 | | k2: usize, |
58 | | /// Columns in second matrix |
59 | | n: usize, |
60 | | }, |
61 | | |
62 | | /// Operation not supported for this tensor type |
63 | | #[error("Operation '{operation}' not supported: {reason}")] |
64 | | UnsupportedOperation { |
65 | | /// Operation name |
66 | | operation: String, |
67 | | /// Reason it's not supported |
68 | | reason: String, |
69 | | }, |
70 | | |
71 | | /// Trueno backend error |
72 | | #[error("Trueno backend error: {0}")] |
73 | | TruenoError(String), |
74 | | |
75 | | /// Index out of bounds |
76 | | #[error("Index out of bounds: index {index} for dimension of size {size}")] |
77 | | IndexOutOfBounds { |
78 | | /// Requested index |
79 | | index: usize, |
80 | | /// Size of dimension |
81 | | size: usize, |
82 | | }, |
83 | | |
84 | | /// Model registry error |
85 | | #[error("Model registry error: {0}")] |
86 | | RegistryError(String), |
87 | | |
88 | | /// Model not found in registry |
89 | | #[error("Model '{0}' not found")] |
90 | | ModelNotFound(String), |
91 | | |
92 | | /// Model already registered |
93 | | #[error("Model '{0}' already registered")] |
94 | | ModelAlreadyExists(String), |
95 | | |
96 | | /// MOE routing error |
97 | | #[error("MOE routing error: {0}")] |
98 | | MoeError(String), |
99 | | |
100 | | /// Expert capacity exceeded |
101 | | #[error("Expert {expert_id} capacity exceeded: {queue_depth}/{capacity}")] |
102 | | ExpertCapacityExceeded { |
103 | | /// Expert index |
104 | | expert_id: usize, |
105 | | /// Current queue depth |
106 | | queue_depth: usize, |
107 | | /// Maximum capacity |
108 | | capacity: usize, |
109 | | }, |
110 | | |
111 | | /// Invalid URI format |
112 | | #[error("Invalid URI: {0}")] |
113 | | InvalidUri(String), |
114 | | |
115 | | /// File format error |
116 | | #[error("Format error: {reason}")] |
117 | | FormatError { |
118 | | /// Reason for format error |
119 | | reason: String, |
120 | | }, |
121 | | |
122 | | /// IO error |
123 | | #[error("IO error: {message}")] |
124 | | IoError { |
125 | | /// Error message |
126 | | message: String, |
127 | | }, |
128 | | |
129 | | /// Connection error (network/HTTP) |
130 | | #[error("Connection error: {0}")] |
131 | | ConnectionError(String), |
132 | | |
133 | | /// GPU compute error |
134 | | #[error("GPU error: {reason}")] |
135 | | GpuError { |
136 | | /// Reason for GPU error |
137 | | reason: String, |
138 | | }, |
139 | | |
140 | | /// Invalid configuration error |
141 | | #[error("Invalid configuration: {0}")] |
142 | | InvalidConfiguration(String), |
143 | | |
144 | | /// Inference execution error |
145 | | #[error("Inference error: {0}")] |
146 | | InferenceError(String), |
147 | | } |
148 | | |
149 | | #[cfg(test)] |
150 | | mod tests { |
151 | | use super::*; |
152 | | |
153 | | #[test] |
154 | 1 | fn test_error_display() { |
155 | 1 | let err = RealizarError::ShapeMismatch { |
156 | 1 | expected: vec![3, 3], |
157 | 1 | actual: vec![2, 2], |
158 | 1 | }; |
159 | 1 | assert!(err.to_string().contains("Shape mismatch")); |
160 | 1 | } |
161 | | |
162 | | #[test] |
163 | 1 | fn test_error_equality() { |
164 | 1 | let err1 = RealizarError::InvalidShape { |
165 | 1 | reason: "Empty shape".to_string(), |
166 | 1 | }; |
167 | 1 | let err2 = RealizarError::InvalidShape { |
168 | 1 | reason: "Empty shape".to_string(), |
169 | 1 | }; |
170 | 1 | assert_eq!(err1, err2); |
171 | 1 | } |
172 | | |
173 | | // ======================================================================== |
174 | | // Coverage Tests: All Error Variants |
175 | | // ======================================================================== |
176 | | |
177 | | #[test] |
178 | 1 | fn test_data_shape_mismatch_display() { |
179 | 1 | let err = RealizarError::DataShapeMismatch { |
180 | 1 | data_size: 10, |
181 | 1 | shape: vec![2, 3], |
182 | 1 | expected: 6, |
183 | 1 | }; |
184 | 1 | let msg = err.to_string(); |
185 | 1 | assert!(msg.contains("10")); |
186 | 1 | assert!(msg.contains("6")); |
187 | 1 | } |
188 | | |
189 | | #[test] |
190 | 1 | fn test_invalid_dimension_display() { |
191 | 1 | let err = RealizarError::InvalidDimension { dim: 5, ndim: 3 }; |
192 | 1 | let msg = err.to_string(); |
193 | 1 | assert!(msg.contains("5")); |
194 | 1 | assert!(msg.contains("3")); |
195 | 1 | } |
196 | | |
197 | | #[test] |
198 | 1 | fn test_matmul_dimension_mismatch_display() { |
199 | 1 | let err = RealizarError::MatmulDimensionMismatch { |
200 | 1 | m: 2, |
201 | 1 | k: 3, |
202 | 1 | k2: 4, |
203 | 1 | n: 5, |
204 | 1 | }; |
205 | 1 | let msg = err.to_string(); |
206 | 1 | assert!(msg.contains("2")); |
207 | 1 | assert!(msg.contains("3")); |
208 | 1 | assert!(msg.contains("4")); |
209 | 1 | assert!(msg.contains("5")); |
210 | 1 | } |
211 | | |
212 | | #[test] |
213 | 1 | fn test_unsupported_operation_display() { |
214 | 1 | let err = RealizarError::UnsupportedOperation { |
215 | 1 | operation: "transpose".to_string(), |
216 | 1 | reason: "not implemented".to_string(), |
217 | 1 | }; |
218 | 1 | let msg = err.to_string(); |
219 | 1 | assert!(msg.contains("transpose")); |
220 | 1 | assert!(msg.contains("not implemented")); |
221 | 1 | } |
222 | | |
223 | | #[test] |
224 | 1 | fn test_trueno_error_display() { |
225 | 1 | let err = RealizarError::TruenoError("kernel failed".to_string()); |
226 | 1 | assert!(err.to_string().contains("kernel failed")); |
227 | 1 | } |
228 | | |
229 | | #[test] |
230 | 1 | fn test_index_out_of_bounds_display() { |
231 | 1 | let err = RealizarError::IndexOutOfBounds { index: 10, size: 5 }; |
232 | 1 | let msg = err.to_string(); |
233 | 1 | assert!(msg.contains("10")); |
234 | 1 | assert!(msg.contains("5")); |
235 | 1 | } |
236 | | |
237 | | #[test] |
238 | 1 | fn test_registry_error_display() { |
239 | 1 | let err = RealizarError::RegistryError("lock failed".to_string()); |
240 | 1 | assert!(err.to_string().contains("lock failed")); |
241 | 1 | } |
242 | | |
243 | | #[test] |
244 | 1 | fn test_model_not_found_display() { |
245 | 1 | let err = RealizarError::ModelNotFound("llama-7b".to_string()); |
246 | 1 | assert!(err.to_string().contains("llama-7b")); |
247 | 1 | } |
248 | | |
249 | | #[test] |
250 | 1 | fn test_model_already_exists_display() { |
251 | 1 | let err = RealizarError::ModelAlreadyExists("phi-2".to_string()); |
252 | 1 | assert!(err.to_string().contains("phi-2")); |
253 | 1 | } |
254 | | |
255 | | #[test] |
256 | 1 | fn test_moe_error_display() { |
257 | 1 | let err = RealizarError::MoeError("routing failed".to_string()); |
258 | 1 | assert!(err.to_string().contains("routing failed")); |
259 | 1 | } |
260 | | |
261 | | #[test] |
262 | 1 | fn test_expert_capacity_exceeded_display() { |
263 | 1 | let err = RealizarError::ExpertCapacityExceeded { |
264 | 1 | expert_id: 3, |
265 | 1 | queue_depth: 10, |
266 | 1 | capacity: 8, |
267 | 1 | }; |
268 | 1 | let msg = err.to_string(); |
269 | 1 | assert!(msg.contains("3")); |
270 | 1 | assert!(msg.contains("10")); |
271 | 1 | assert!(msg.contains("8")); |
272 | 1 | } |
273 | | |
274 | | #[test] |
275 | 1 | fn test_invalid_uri_display() { |
276 | 1 | let err = RealizarError::InvalidUri("bad://url".to_string()); |
277 | 1 | assert!(err.to_string().contains("bad://url")); |
278 | 1 | } |
279 | | |
280 | | #[test] |
281 | 1 | fn test_format_error_display() { |
282 | 1 | let err = RealizarError::FormatError { |
283 | 1 | reason: "invalid header".to_string(), |
284 | 1 | }; |
285 | 1 | assert!(err.to_string().contains("invalid header")); |
286 | 1 | } |
287 | | |
288 | | #[test] |
289 | 1 | fn test_io_error_display() { |
290 | 1 | let err = RealizarError::IoError { |
291 | 1 | message: "file not found".to_string(), |
292 | 1 | }; |
293 | 1 | assert!(err.to_string().contains("file not found")); |
294 | 1 | } |
295 | | |
296 | | #[test] |
297 | 1 | fn test_connection_error_display() { |
298 | 1 | let err = RealizarError::ConnectionError("timeout".to_string()); |
299 | 1 | assert!(err.to_string().contains("timeout")); |
300 | 1 | } |
301 | | |
302 | | #[test] |
303 | 1 | fn test_gpu_error_display() { |
304 | 1 | let err = RealizarError::GpuError { |
305 | 1 | reason: "out of memory".to_string(), |
306 | 1 | }; |
307 | 1 | assert!(err.to_string().contains("out of memory")); |
308 | 1 | } |
309 | | |
310 | | #[test] |
311 | 1 | fn test_invalid_configuration_display() { |
312 | 1 | let err = RealizarError::InvalidConfiguration("missing field".to_string()); |
313 | 1 | assert!(err.to_string().contains("missing field")); |
314 | 1 | } |
315 | | |
316 | | #[test] |
317 | 1 | fn test_inference_error_display() { |
318 | 1 | let err = RealizarError::InferenceError("model failed".to_string()); |
319 | 1 | assert!(err.to_string().contains("model failed")); |
320 | 1 | } |
321 | | |
322 | | #[test] |
323 | 1 | fn test_error_debug() { |
324 | 1 | let err = RealizarError::ShapeMismatch { |
325 | 1 | expected: vec![1, 2], |
326 | 1 | actual: vec![3, 4], |
327 | 1 | }; |
328 | 1 | let debug = format!("{:?}", err); |
329 | 1 | assert!(debug.contains("ShapeMismatch")); |
330 | 1 | } |
331 | | |
332 | | #[test] |
333 | 1 | fn test_error_clone() { |
334 | 1 | let err = RealizarError::InvalidShape { |
335 | 1 | reason: "test".to_string(), |
336 | 1 | }; |
337 | 1 | let cloned = err.clone(); |
338 | 1 | assert_eq!(err, cloned); |
339 | 1 | } |
340 | | } |