oar_ocr/core/
macros.rs

1//! Macros for the OCR pipeline.
2//!
3//! This module provides utility macros to reduce code duplication across
4//! the OCR pipeline, particularly for builder patterns and metrics collection.
5
6/// Macro to handle optional nested config initialization in builders.
7///
8/// This macro eliminates the repeated pattern of:
9/// ```rust,ignore
10/// if self.config.field.is_none() {
11///     self.config.field = Some(Type::new());
12/// }
13/// ```
14///
15/// # Usage
16///
17/// ```rust,ignore
18/// // Instead of:
19/// if self.config.orientation.is_none() {
20///     self.config.orientation = Some(DocOrientationClassifierConfig::new());
21/// }
22/// if let Some(ref mut config) = self.config.orientation {
23///     config.confidence_threshold = Some(threshold);
24/// }
25///
26/// // Use:
27/// with_nested!(self.config.orientation, DocOrientationClassifierConfig, config => {
28///     config.confidence_threshold = Some(threshold);
29/// });
30/// ```
31#[macro_export]
32macro_rules! with_nested {
33    ($field:expr, $type:ty, $var:ident => $body:block) => {
34        if $field.is_none() {
35            $field = Some(<$type>::new());
36        }
37        if let Some(ref mut $var) = $field {
38            $body
39        }
40    };
41}
42
43/// Macro to create pre-populated StageMetrics with common patterns.
44///
45/// This macro reduces duplication in metrics construction across stages.
46///
47/// # Usage
48///
49/// ```rust,ignore
50/// // Instead of:
51/// StageMetrics::new(success_count, failure_count)
52///     .with_processing_time(start_time.elapsed())
53///     .with_info("stage", "cropping")
54///     .with_info("batch_size", batch_size.to_string())
55///     .with_info("parallel", parallel.to_string())
56///
57/// // Use:
58/// metrics!(success_count, failure_count, start_time; stage = "cropping", batch_size = batch_size, parallel = parallel)
59/// // Or without timing:
60/// metrics!(success_count, failure_count; stage = "cropping", batch_size = batch_size)
61/// ```
62#[macro_export]
63macro_rules! metrics {
64    // With timing
65    ($success:expr, $failure:expr, $start_time:expr; $($key:ident = $value:expr),*) => {
66        {
67            let mut metrics = $crate::pipeline::stages::StageMetrics::new($success, $failure);
68            metrics = metrics.with_processing_time($start_time.elapsed());
69            $(
70                metrics = metrics.with_info(stringify!($key), $value.to_string());
71            )*
72            metrics
73        }
74    };
75    // Without timing
76    ($success:expr, $failure:expr; $($key:ident = $value:expr),*) => {
77        {
78            let mut metrics = $crate::pipeline::stages::StageMetrics::new($success, $failure);
79            $(
80                metrics = metrics.with_info(stringify!($key), $value.to_string());
81            )*
82            metrics
83        }
84    };
85}
86
87/// Comprehensive builder macro for generating common builder method patterns.
88///
89/// This macro generates multiple types of builder methods to reduce code duplication:
90/// 1. Simple setters for direct field assignment
91/// 2. Nested config setters using the `with_nested!` macro
92/// 3. Enable/disable methods for optional features
93/// 4. Dynamic batching configuration methods
94///
95/// # Usage
96///
97/// ```rust,ignore
98/// impl_complete_builder! {
99///     builder: MyBuilder,
100///     config_field: config,
101///
102///     // Simple setters
103///     simple_setters: {
104///         field_name: FieldType => "Documentation for the setter",
105///     },
106///
107///     // Nested config setters
108///     nested_setters: {
109///         config_path: ConfigType => {
110///             field_name: FieldType => "Documentation",
111///         },
112///     },
113///
114///     // Enable/disable methods
115///     enable_methods: {
116///         method_name => config_field: DefaultType => "Documentation",
117///     },
118/// }
119/// ```
120#[macro_export]
121macro_rules! impl_complete_builder {
122    // Simple setters only
123    (
124        builder: $builder:ident,
125        config_field: $config_field:ident,
126        simple_setters: {
127            $($simple_field:ident: $simple_type:ty => $simple_doc:literal),* $(,)?
128        }
129    ) => {
130        impl $builder {
131            $(
132                #[doc = $simple_doc]
133                pub fn $simple_field(mut self, value: $simple_type) -> Self {
134                    self.$config_field.$simple_field = Some(value);
135                    self
136                }
137            )*
138        }
139    };
140
141    // Nested setters only
142    (
143        builder: $builder:ident,
144        config_field: $config_field:ident,
145        nested_setters: {
146            $($nested_path:ident: $nested_type:ty => {
147                $($nested_field:ident: $nested_field_type:ty => $nested_doc:literal),* $(,)?
148            }),* $(,)?
149        }
150    ) => {
151        impl $builder {
152            $($(
153                #[doc = $nested_doc]
154                pub fn $nested_field(mut self, value: $nested_field_type) -> Self {
155                    $crate::with_nested!(self.$config_field.$nested_path, $nested_type, config => {
156                        config.$nested_field = Some(value);
157                    });
158                    self
159                }
160            )*)*
161        }
162    };
163
164    // Enable methods only
165    (
166        builder: $builder:ident,
167        config_field: $config_field:ident,
168        enable_methods: {
169            $($enable_method:ident => $enable_field:ident: $enable_type:ty => $enable_doc:literal),* $(,)?
170        }
171    ) => {
172        impl $builder {
173            $(
174                #[doc = $enable_doc]
175                pub fn $enable_method(mut self) -> Self {
176                    self.$config_field.$enable_field = Some(<$enable_type>::default());
177                    self
178                }
179            )*
180        }
181    };
182}
183
184/// Macro to implement `new()` and `with_common()` for config structs with per-module defaults.
185#[macro_export]
186macro_rules! impl_config_new_and_with_common {
187    (
188        $Config:ident,
189        common_defaults: ($model_name_opt:expr, $batch_size_opt:expr),
190        fields: { $( $field:ident : $default_expr:expr ),* $(,)? }
191    ) => {
192        impl $Config {
193            /// Creates a new config instance with default values
194            pub fn new() -> Self {
195                Self {
196                    common: $crate::core::config::builder::CommonBuilderConfig::with_defaults(
197                        $model_name_opt, $batch_size_opt
198                    ),
199                    $( $field: $default_expr ),*
200                }
201            }
202            /// Creates a new config instance using provided common configuration
203            pub fn with_common(common: $crate::core::config::builder::CommonBuilderConfig) -> Self {
204                Self {
205                    common,
206                    $( $field: $default_expr ),*
207                }
208            }
209        }
210    };
211}
212
213/// Macro to implement common builder methods for structs with a `CommonBuilderConfig` field.
214#[macro_export]
215macro_rules! impl_common_builder_methods {
216    ($Builder:ident, $common_field:ident) => {
217        impl $Builder {
218            /// Sets the model path
219            pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
220                self.$common_field = self.$common_field.model_path(model_path);
221                self
222            }
223            /// Sets the model name
224            pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
225                self.$common_field = self.$common_field.model_name(model_name);
226                self
227            }
228            /// Sets the batch size
229            pub fn batch_size(mut self, batch_size: usize) -> Self {
230                self.$common_field = self.$common_field.batch_size(batch_size);
231                self
232            }
233            /// Enables or disables logging
234            pub fn enable_logging(mut self, enable: bool) -> Self {
235                self.$common_field = self.$common_field.enable_logging(enable);
236                self
237            }
238            /// Sets the ONNX Runtime session configuration
239            pub fn ort_session(
240                mut self,
241                config: $crate::core::config::onnx::OrtSessionConfig,
242            ) -> Self {
243                self.$common_field = self.$common_field.ort_session(config);
244                self
245            }
246            /// Sets the session pool size for concurrent predictions (>=1)
247            pub fn session_pool_size(mut self, size: usize) -> Self {
248                self.$common_field = self.$common_field.session_pool_size(size);
249                self
250            }
251        }
252    };
253}
254
255/// Macro to inject common builder methods into an existing `impl Builder` block.
256/// Use this inside `impl YourBuilder { ... }` and pass the field name that holds
257/// `CommonBuilderConfig` (e.g., `common`).
258#[macro_export]
259macro_rules! common_builder_methods {
260    ($common_field:ident) => {
261        /// Sets the model path
262        pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
263            self.$common_field = self.$common_field.model_path(model_path);
264            self
265        }
266        /// Sets the model name
267        pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
268            self.$common_field = self.$common_field.model_name(model_name);
269            self
270        }
271        /// Sets the batch size
272        pub fn batch_size(mut self, batch_size: usize) -> Self {
273            self.$common_field = self.$common_field.batch_size(batch_size);
274            self
275        }
276        /// Enables or disables logging
277        pub fn enable_logging(mut self, enable: bool) -> Self {
278            self.$common_field = self.$common_field.enable_logging(enable);
279            self
280        }
281        /// Sets the ONNX Runtime session configuration
282        pub fn ort_session(mut self, config: $crate::core::config::onnx::OrtSessionConfig) -> Self {
283            self.$common_field = self.$common_field.ort_session(config);
284            self
285        }
286        /// Sets the session pool size for concurrent predictions (>=1)
287        pub fn session_pool_size(mut self, size: usize) -> Self {
288            self.$common_field = self.$common_field.session_pool_size(size);
289            self
290        }
291    };
292}
293
294#[cfg(test)]
295mod tests {
296
297    // Test configuration structs
298    #[derive(Debug, Default)]
299    struct TestConfig {
300        simple_field: Option<String>,
301        nested_config: Option<NestedConfig>,
302        enable_field: Option<EnabledFeature>,
303    }
304
305    #[derive(Debug, Default)]
306    struct NestedConfig {
307        nested_field: Option<i32>,
308    }
309
310    impl NestedConfig {
311        fn new() -> Self {
312            Self::default()
313        }
314    }
315
316    #[derive(Debug, Default)]
317    struct EnabledFeature {
318        #[allow(dead_code)]
319        enabled: bool,
320    }
321
322    // Test builder struct
323    #[derive(Debug)]
324    struct TestBuilder {
325        config: TestConfig,
326    }
327
328    impl TestBuilder {
329        fn new() -> Self {
330            Self {
331                config: TestConfig::default(),
332            }
333        }
334
335        fn get_config(&self) -> &TestConfig {
336            &self.config
337        }
338    }
339
340    // Apply the macro to generate builder methods (separate calls for each type)
341    impl_complete_builder! {
342        builder: TestBuilder,
343        config_field: config,
344        simple_setters: {
345            simple_field: String => "Sets a simple field value",
346        }
347    }
348
349    impl_complete_builder! {
350        builder: TestBuilder,
351        config_field: config,
352        nested_setters: {
353            nested_config: NestedConfig => {
354                nested_field: i32 => "Sets a nested field value",
355            },
356        }
357    }
358
359    impl_complete_builder! {
360        builder: TestBuilder,
361        config_field: config,
362        enable_methods: {
363            enable_feature => enable_field: EnabledFeature => "Enables a feature with default configuration",
364        }
365    }
366
367    #[test]
368    fn test_impl_complete_builder_nested_setter() {
369        let builder = TestBuilder::new().nested_field(42);
370
371        assert!(builder.get_config().nested_config.is_some());
372        assert_eq!(
373            builder
374                .get_config()
375                .nested_config
376                .as_ref()
377                .unwrap()
378                .nested_field,
379            Some(42)
380        );
381    }
382
383    #[test]
384    fn test_impl_complete_builder_enable_method() {
385        let builder = TestBuilder::new().enable_feature();
386
387        assert!(builder.get_config().enable_field.is_some());
388    }
389
390    #[test]
391    fn test_impl_complete_builder_chaining() {
392        let builder = TestBuilder::new()
393            .simple_field("test".to_string())
394            .nested_field(123)
395            .enable_feature();
396
397        let config = builder.get_config();
398        assert_eq!(config.simple_field, Some("test".to_string()));
399        assert!(config.nested_config.is_some());
400        assert_eq!(
401            config.nested_config.as_ref().unwrap().nested_field,
402            Some(123)
403        );
404        assert!(config.enable_field.is_some());
405    }
406}