Skip to main content

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}