oximedia_cv/error.rs
1//! Error types for computer vision operations.
2//!
3//! This module provides the [`CvError`] type which represents all errors
4//! that can occur during computer vision operations, and the [`CvResult`]
5//! type alias for convenient use.
6
7use oximedia_core::OxiError;
8
9/// Error type for computer vision operations.
10///
11/// This enum covers all possible errors that can occur during image processing,
12/// detection, and transformation operations.
13///
14/// # Examples
15///
16/// ```
17/// use oximedia_cv::error::{CvError, CvResult};
18///
19/// fn process_image(width: u32, height: u32) -> CvResult<()> {
20/// if width == 0 || height == 0 {
21/// return Err(CvError::InvalidDimensions { width, height });
22/// }
23/// Ok(())
24/// }
25/// ```
26#[derive(Debug, thiserror::Error)]
27pub enum CvError {
28 /// Invalid image dimensions (width or height is zero).
29 #[error("Invalid image dimensions: {width}x{height}")]
30 InvalidDimensions {
31 /// Image width.
32 width: u32,
33 /// Image height.
34 height: u32,
35 },
36
37 /// Invalid region of interest.
38 #[error("Invalid ROI: ({x}, {y}, {width}, {height})")]
39 InvalidRoi {
40 /// ROI x coordinate.
41 x: u32,
42 /// ROI y coordinate.
43 y: u32,
44 /// ROI width.
45 width: u32,
46 /// ROI height.
47 height: u32,
48 },
49
50 /// Invalid kernel size for filter operations.
51 #[error("Invalid kernel size: {size} (must be odd and >= 3)")]
52 InvalidKernelSize {
53 /// The invalid kernel size.
54 size: usize,
55 },
56
57 /// Color space conversion error.
58 #[error("Color conversion error: {message}")]
59 ColorConversion {
60 /// Description of the error.
61 message: String,
62 },
63
64 /// Unsupported pixel format.
65 #[error("Unsupported pixel format: {format}")]
66 UnsupportedFormat {
67 /// The unsupported format name.
68 format: String,
69 },
70
71 /// Detection failed.
72 #[error("Detection failed: {message}")]
73 DetectionFailed {
74 /// Description of the failure.
75 message: String,
76 },
77
78 /// Transform computation failed.
79 #[error("Transform error: {message}")]
80 TransformError {
81 /// Description of the error.
82 message: String,
83 },
84
85 /// Tracking operation failed.
86 #[error("Tracking error: {message}")]
87 TrackingError {
88 /// Description of the error.
89 message: String,
90 },
91
92 /// Insufficient data for operation.
93 #[error("Insufficient data: expected {expected} bytes, got {actual}")]
94 InsufficientData {
95 /// Expected number of bytes.
96 expected: usize,
97 /// Actual number of bytes.
98 actual: usize,
99 },
100
101 /// Matrix operation error.
102 #[error("Matrix error: {message}")]
103 MatrixError {
104 /// Description of the error.
105 message: String,
106 },
107
108 /// Invalid parameter value.
109 #[error("Invalid parameter: {name} = {value}")]
110 InvalidParameter {
111 /// Parameter name.
112 name: String,
113 /// Invalid value description.
114 value: String,
115 },
116
117 /// Core error from oximedia-core.
118 #[error("Core error: {0}")]
119 Core(#[from] OxiError),
120
121 /// ONNX Runtime error.
122 #[error("ONNX Runtime error: {message}")]
123 OnnxRuntime {
124 /// Description of the error.
125 message: String,
126 },
127
128 /// Model loading error.
129 #[error("Model load error: {message}")]
130 ModelLoad {
131 /// Description of the error.
132 message: String,
133 },
134
135 /// Tensor operation error.
136 #[error("Tensor error: {message}")]
137 TensorError {
138 /// Description of the error.
139 message: String,
140 },
141
142 /// Shape mismatch error.
143 #[error("Shape mismatch: expected {expected:?}, got {actual:?}")]
144 ShapeMismatch {
145 /// Expected shape.
146 expected: Vec<usize>,
147 /// Actual shape.
148 actual: Vec<usize>,
149 },
150
151 /// I/O error.
152 #[error("I/O error: {0}")]
153 Io(#[from] std::io::Error),
154}
155
156impl CvError {
157 /// Creates a new invalid dimensions error.
158 ///
159 /// # Examples
160 ///
161 /// ```
162 /// use oximedia_cv::error::CvError;
163 ///
164 /// let err = CvError::invalid_dimensions(0, 100);
165 /// assert!(matches!(err, CvError::InvalidDimensions { width: 0, height: 100 }));
166 /// ```
167 #[must_use]
168 pub const fn invalid_dimensions(width: u32, height: u32) -> Self {
169 Self::InvalidDimensions { width, height }
170 }
171
172 /// Creates a new invalid ROI error.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use oximedia_cv::error::CvError;
178 ///
179 /// let err = CvError::invalid_roi(10, 20, 0, 50);
180 /// assert!(matches!(err, CvError::InvalidRoi { .. }));
181 /// ```
182 #[must_use]
183 pub const fn invalid_roi(x: u32, y: u32, width: u32, height: u32) -> Self {
184 Self::InvalidRoi {
185 x,
186 y,
187 width,
188 height,
189 }
190 }
191
192 /// Creates a new invalid kernel size error.
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// use oximedia_cv::error::CvError;
198 ///
199 /// let err = CvError::invalid_kernel_size(4);
200 /// assert!(matches!(err, CvError::InvalidKernelSize { size: 4 }));
201 /// ```
202 #[must_use]
203 pub const fn invalid_kernel_size(size: usize) -> Self {
204 Self::InvalidKernelSize { size }
205 }
206
207 /// Creates a new color conversion error.
208 ///
209 /// # Examples
210 ///
211 /// ```
212 /// use oximedia_cv::error::CvError;
213 ///
214 /// let err = CvError::color_conversion("Invalid color values");
215 /// assert!(matches!(err, CvError::ColorConversion { .. }));
216 /// ```
217 #[must_use]
218 pub fn color_conversion(message: impl Into<String>) -> Self {
219 Self::ColorConversion {
220 message: message.into(),
221 }
222 }
223
224 /// Creates a new unsupported format error.
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// use oximedia_cv::error::CvError;
230 ///
231 /// let err = CvError::unsupported_format("YUV444");
232 /// assert!(matches!(err, CvError::UnsupportedFormat { .. }));
233 /// ```
234 #[must_use]
235 pub fn unsupported_format(format: impl Into<String>) -> Self {
236 Self::UnsupportedFormat {
237 format: format.into(),
238 }
239 }
240
241 /// Creates a new detection failed error.
242 ///
243 /// # Examples
244 ///
245 /// ```
246 /// use oximedia_cv::error::CvError;
247 ///
248 /// let err = CvError::detection_failed("No faces found");
249 /// assert!(matches!(err, CvError::DetectionFailed { .. }));
250 /// ```
251 #[must_use]
252 pub fn detection_failed(message: impl Into<String>) -> Self {
253 Self::DetectionFailed {
254 message: message.into(),
255 }
256 }
257
258 /// Creates a new transform error.
259 ///
260 /// # Examples
261 ///
262 /// ```
263 /// use oximedia_cv::error::CvError;
264 ///
265 /// let err = CvError::transform_error("Matrix is singular");
266 /// assert!(matches!(err, CvError::TransformError { .. }));
267 /// ```
268 #[must_use]
269 pub fn transform_error(message: impl Into<String>) -> Self {
270 Self::TransformError {
271 message: message.into(),
272 }
273 }
274
275 /// Creates a new tracking error.
276 ///
277 /// # Examples
278 ///
279 /// ```
280 /// use oximedia_cv::error::CvError;
281 ///
282 /// let err = CvError::tracking_error("Tracker not initialized");
283 /// assert!(matches!(err, CvError::TrackingError { .. }));
284 /// ```
285 #[must_use]
286 pub fn tracking_error(message: impl Into<String>) -> Self {
287 Self::TrackingError {
288 message: message.into(),
289 }
290 }
291
292 /// Creates a new insufficient data error.
293 ///
294 /// # Examples
295 ///
296 /// ```
297 /// use oximedia_cv::error::CvError;
298 ///
299 /// let err = CvError::insufficient_data(1024, 512);
300 /// assert!(matches!(err, CvError::InsufficientData { expected: 1024, actual: 512 }));
301 /// ```
302 #[must_use]
303 pub const fn insufficient_data(expected: usize, actual: usize) -> Self {
304 Self::InsufficientData { expected, actual }
305 }
306
307 /// Creates a new matrix error.
308 ///
309 /// # Examples
310 ///
311 /// ```
312 /// use oximedia_cv::error::CvError;
313 ///
314 /// let err = CvError::matrix_error("Matrix dimensions mismatch");
315 /// assert!(matches!(err, CvError::MatrixError { .. }));
316 /// ```
317 #[must_use]
318 pub fn matrix_error(message: impl Into<String>) -> Self {
319 Self::MatrixError {
320 message: message.into(),
321 }
322 }
323
324 /// Creates a new invalid parameter error.
325 ///
326 /// # Examples
327 ///
328 /// ```
329 /// use oximedia_cv::error::CvError;
330 ///
331 /// let err = CvError::invalid_parameter("sigma", "-1.0");
332 /// assert!(matches!(err, CvError::InvalidParameter { .. }));
333 /// ```
334 #[must_use]
335 pub fn invalid_parameter(name: impl Into<String>, value: impl Into<String>) -> Self {
336 Self::InvalidParameter {
337 name: name.into(),
338 value: value.into(),
339 }
340 }
341
342 /// Creates a new ONNX Runtime error.
343 ///
344 /// # Examples
345 ///
346 /// ```
347 /// use oximedia_cv::error::CvError;
348 ///
349 /// let err = CvError::onnx_runtime("Session initialization failed");
350 /// assert!(matches!(err, CvError::OnnxRuntime { .. }));
351 /// ```
352 #[must_use]
353 pub fn onnx_runtime(message: impl Into<String>) -> Self {
354 Self::OnnxRuntime {
355 message: message.into(),
356 }
357 }
358
359 /// Creates a new model load error.
360 ///
361 /// # Examples
362 ///
363 /// ```
364 /// use oximedia_cv::error::CvError;
365 ///
366 /// let err = CvError::model_load("File not found");
367 /// assert!(matches!(err, CvError::ModelLoad { .. }));
368 /// ```
369 #[must_use]
370 pub fn model_load(message: impl Into<String>) -> Self {
371 Self::ModelLoad {
372 message: message.into(),
373 }
374 }
375
376 /// Creates a new tensor error.
377 ///
378 /// # Examples
379 ///
380 /// ```
381 /// use oximedia_cv::error::CvError;
382 ///
383 /// let err = CvError::tensor_error("Invalid data type");
384 /// assert!(matches!(err, CvError::TensorError { .. }));
385 /// ```
386 #[must_use]
387 pub fn tensor_error(message: impl Into<String>) -> Self {
388 Self::TensorError {
389 message: message.into(),
390 }
391 }
392
393 /// Creates a new shape mismatch error.
394 ///
395 /// # Examples
396 ///
397 /// ```
398 /// use oximedia_cv::error::CvError;
399 ///
400 /// let err = CvError::shape_mismatch(vec![1, 3, 224, 224], vec![1, 3, 256, 256]);
401 /// assert!(matches!(err, CvError::ShapeMismatch { .. }));
402 /// ```
403 #[must_use]
404 pub fn shape_mismatch(expected: Vec<usize>, actual: Vec<usize>) -> Self {
405 Self::ShapeMismatch { expected, actual }
406 }
407}
408
409/// Result type alias for computer vision operations.
410///
411/// This is a convenience alias for `Result<T, CvError>`.
412///
413/// # Examples
414///
415/// ```
416/// use oximedia_cv::error::CvResult;
417///
418/// fn process() -> CvResult<u32> {
419/// Ok(42)
420/// }
421/// ```
422pub type CvResult<T> = Result<T, CvError>;
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn test_invalid_dimensions() {
430 let err = CvError::invalid_dimensions(0, 100);
431 assert!(matches!(
432 err,
433 CvError::InvalidDimensions {
434 width: 0,
435 height: 100
436 }
437 ));
438 let msg = format!("{err}");
439 assert!(msg.contains("0x100"));
440 }
441
442 #[test]
443 fn test_invalid_roi() {
444 let err = CvError::invalid_roi(10, 20, 0, 50);
445 assert!(matches!(
446 err,
447 CvError::InvalidRoi {
448 x: 10,
449 y: 20,
450 width: 0,
451 height: 50
452 }
453 ));
454 }
455
456 #[test]
457 fn test_invalid_kernel_size() {
458 let err = CvError::invalid_kernel_size(4);
459 assert!(matches!(err, CvError::InvalidKernelSize { size: 4 }));
460 let msg = format!("{err}");
461 assert!(msg.contains('4'));
462 }
463
464 #[test]
465 fn test_color_conversion_error() {
466 let err = CvError::color_conversion("Invalid color");
467 assert!(matches!(err, CvError::ColorConversion { .. }));
468 let msg = format!("{err}");
469 assert!(msg.contains("Invalid color"));
470 }
471
472 #[test]
473 fn test_unsupported_format() {
474 let err = CvError::unsupported_format("YUV444");
475 assert!(format!("{err}").contains("YUV444"));
476 }
477
478 #[test]
479 fn test_detection_failed() {
480 let err = CvError::detection_failed("No faces");
481 assert!(format!("{err}").contains("No faces"));
482 }
483
484 #[test]
485 fn test_transform_error() {
486 let err = CvError::transform_error("Singular matrix");
487 assert!(format!("{err}").contains("Singular matrix"));
488 }
489
490 #[test]
491 fn test_insufficient_data() {
492 let err = CvError::insufficient_data(1024, 512);
493 let msg = format!("{err}");
494 assert!(msg.contains("1024"));
495 assert!(msg.contains("512"));
496 }
497
498 #[test]
499 fn test_matrix_error() {
500 let err = CvError::matrix_error("Dimension mismatch");
501 assert!(format!("{err}").contains("Dimension mismatch"));
502 }
503
504 #[test]
505 fn test_invalid_parameter() {
506 let err = CvError::invalid_parameter("sigma", "-1.0");
507 let msg = format!("{err}");
508 assert!(msg.contains("sigma"));
509 assert!(msg.contains("-1.0"));
510 }
511}