Skip to main content

oar_ocr_core/core/traits/
standard.rs

1//! Core traits for sampling and image reading.
2//!
3//! This module provides traits used throughout the OCR pipeline for batch
4//! sampling and image I/O operations.
5
6use crate::core::errors::OCRError;
7use image::RgbImage;
8use std::path::Path;
9
10/// Trait for sampling data into batches.
11///
12/// This trait defines the interface for sampling data into batches for processing.
13pub trait Sampler<T> {
14    /// The type of batch data produced by this sampler.
15    type BatchData;
16
17    /// Samples the given data into batches.
18    ///
19    /// # Arguments
20    ///
21    /// * `data` - The data to sample.
22    ///
23    /// # Returns
24    ///
25    /// A vector of batch data.
26    fn sample(&self, data: Vec<T>) -> Vec<Self::BatchData>;
27
28    /// Samples the given slice of data into batches.
29    ///
30    /// # Arguments
31    ///
32    /// * `data` - The slice of data to sample.
33    ///
34    /// # Returns
35    ///
36    /// A vector of batch data.
37    ///
38    /// # Constraints
39    ///
40    /// * `T` must implement Clone.
41    fn sample_slice(&self, data: &[T]) -> Vec<Self::BatchData>
42    where
43        T: Clone,
44    {
45        self.sample(data.to_vec())
46    }
47
48    /// Samples the given iterator of data into batches.
49    ///
50    /// # Arguments
51    ///
52    /// * `data` - The iterator of data to sample.
53    ///
54    /// # Returns
55    ///
56    /// A vector of batch data.
57    ///
58    /// # Constraints
59    ///
60    /// * `I` must implement IntoIterator<Item = T>.
61    fn sample_iter<I>(&self, data: I) -> Vec<Self::BatchData>
62    where
63        I: IntoIterator<Item = T>,
64    {
65        self.sample(data.into_iter().collect())
66    }
67}
68
69/// Trait for reading images.
70///
71/// This trait defines the interface for reading images from paths.
72pub trait ImageReader {
73    /// The error type of this image reader.
74    type Error;
75
76    /// Applies the image reader to the given paths.
77    ///
78    /// # Arguments
79    ///
80    /// * `imgs` - An iterator of paths to the images to read.
81    ///
82    /// # Returns
83    ///
84    /// A Result containing a vector of RGB images or an error.
85    ///
86    /// # Constraints
87    ///
88    /// * `P` must implement `AsRef<Path>` + Send + Sync.
89    fn apply<P: AsRef<Path> + Send + Sync>(
90        &self,
91        imgs: impl IntoIterator<Item = P>,
92    ) -> Result<Vec<RgbImage>, Self::Error>;
93
94    /// Reads a single image from the given path.
95    ///
96    /// # Arguments
97    ///
98    /// * `img_path` - The path to the image to read.
99    ///
100    /// # Returns
101    ///
102    /// A Result containing the RGB image or an error.
103    ///
104    /// # Constraints
105    ///
106    /// * `P` must implement `AsRef<Path>` + Send + Sync.
107    fn read_single<P: AsRef<Path> + Send + Sync>(
108        &self,
109        img_path: P,
110    ) -> Result<RgbImage, Self::Error>
111    where
112        Self::Error: From<OCRError>,
113    {
114        let mut results = self.apply(std::iter::once(img_path))?;
115        results.pop().ok_or_else(|| {
116            // Create a proper error instead of panicking
117            OCRError::invalid_input("ImageReader::apply returned empty result for single image")
118                .into()
119        })
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126    use crate::core::OCRError;
127    use image::RgbImage;
128    use std::path::Path;
129
130    /// Mock ImageReader that always returns empty results to test error handling
131    struct MockEmptyImageReader;
132
133    impl ImageReader for MockEmptyImageReader {
134        type Error = OCRError;
135
136        fn apply<P: AsRef<Path> + Send + Sync>(
137            &self,
138            _imgs: impl IntoIterator<Item = P>,
139        ) -> Result<Vec<RgbImage>, Self::Error> {
140            // Always return empty vector to trigger the error condition
141            Ok(Vec::new())
142        }
143    }
144
145    #[test]
146    fn test_read_single_handles_empty_result_properly() {
147        let reader = MockEmptyImageReader;
148        let result = reader.read_single("test_path.jpg");
149
150        // Should return an error instead of panicking
151        assert!(result.is_err());
152
153        // Check that it's the correct error type
154        let err = result.unwrap_err();
155        if let OCRError::InvalidInput { message } = err {
156            assert!(message.contains("ImageReader::apply returned empty result for single image"));
157        } else {
158            panic!("Expected InvalidInput error, got {:?}", err);
159        }
160    }
161}