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}