Skip to main content

oar_ocr_core/predictors/
core.rs

1//! Core predictor functionality
2//!
3//! This module provides a generic predictor implementation that can be reused
4//! across all task-specific predictors, eliminating boilerplate code.
5
6use crate::core::OcrResult;
7use crate::core::traits::adapter::ModelAdapter;
8use crate::core::traits::task::Task;
9
10/// Generic task predictor core.
11///
12/// This struct encapsulates the common pattern used across all predictors:
13/// holding an adapter, task instance, and configuration, and executing
14/// predictions through the adapter with proper validation.
15///
16/// # Type Parameters
17///
18/// * `T` - The task type that implements the `Task` trait
19pub struct TaskPredictorCore<T: Task> {
20    /// The model adapter for this task
21    pub(crate) adapter: Box<dyn ModelAdapter<Task = T>>,
22    /// The task instance for validation
23    pub(crate) task: T,
24    /// The task configuration
25    pub(crate) config: T::Config,
26}
27
28impl<T: Task> TaskPredictorCore<T> {
29    /// Creates a new task predictor core.
30    ///
31    /// # Arguments
32    ///
33    /// * `adapter` - The model adapter to use for predictions
34    /// * `task` - The task instance for validation
35    /// * `config` - The task configuration
36    pub fn new(adapter: Box<dyn ModelAdapter<Task = T>>, task: T, config: T::Config) -> Self {
37        Self {
38            adapter,
39            task,
40            config,
41        }
42    }
43
44    /// Executes prediction on the given input.
45    ///
46    /// This method performs the complete validation and execution pipeline:
47    /// 1. Validate input using the task's validation logic
48    /// 2. Execute inference through the adapter
49    /// 3. Validate output using the task's validation logic
50    ///
51    /// # Arguments
52    ///
53    /// * `input` - The input data for prediction
54    ///
55    /// # Returns
56    ///
57    /// The task output on success, or an error if validation or execution fails.
58    pub fn predict(&self, input: T::Input) -> OcrResult<T::Output> {
59        // 1. Validate input
60        self.task.validate_input(&input)?;
61
62        // 2. Execute prediction through the adapter
63        let output = self.adapter.execute(input, Some(&self.config))?;
64
65        // 3. Validate output
66        self.task.validate_output(&output)?;
67
68        Ok(output)
69    }
70
71    /// Returns a reference to the current configuration.
72    pub fn config(&self) -> &T::Config {
73        &self.config
74    }
75
76    /// Returns a mutable reference to the configuration.
77    ///
78    /// This allows modifying the configuration after the predictor is created,
79    /// though creating a new predictor with a different configuration is generally
80    /// preferred for clarity.
81    pub fn config_mut(&mut self) -> &mut T::Config {
82        &mut self.config
83    }
84
85    /// Returns a reference to the task instance.
86    pub fn task(&self) -> &T {
87        &self.task
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use crate::domain::tasks::text_detection::{TextDetectionConfig, TextDetectionTask};
95
96    #[test]
97    fn test_task_predictor_core_creation() {
98        // This test just verifies the type compiles
99        // We can't actually create an adapter without model files
100        let _check = || -> Option<TaskPredictorCore<TextDetectionTask>> { None };
101    }
102
103    #[test]
104    fn test_config_accessors() {
105        // Verify config() and config_mut() compile with correct types
106        let _check = || {
107            let mut core: Option<TaskPredictorCore<TextDetectionTask>> = None;
108            if let Some(c) = core.as_ref() {
109                let _cfg: &TextDetectionConfig = c.config();
110            }
111            if let Some(c) = core.as_mut() {
112                let _cfg: &mut TextDetectionConfig = c.config_mut();
113            }
114        };
115    }
116}