Skip to main content

oar_ocr_core/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/// Central task registry macro that defines all tasks in a single location.
7///
8/// This macro uses the "callback pattern" - it takes a callback macro name and
9/// invokes it with the task registry data. Different consumers can process
10/// the same data differently.
11///
12/// # Task Entry Format
13///
14/// Each task is defined as:
15/// ```text
16/// TaskName {
17///     output: OutputType,    // fully qualified path
18///     adapter: AdapterType,  // fully qualified path
19///     constructor: constructor_name,
20///     conversion: into_method_name,
21///     doc: "Documentation string for IDE/rustdoc",
22/// }
23/// ```
24///
25/// The `TaskDefinition` trait provides task metadata (name, doc, empty()) for runtime.
26/// The `doc` field in the registry is used for `#[doc]` attributes on enum variants.
27#[macro_export]
28macro_rules! with_task_registry {
29    ($callback:path) => {
30        $callback! {
31            TextDetection {
32                output: $crate::domain::tasks::TextDetectionOutput,
33                adapter: $crate::domain::adapters::TextDetectionAdapter,
34                constructor: text_detection,
35                conversion: into_text_detection,
36                doc: "Text detection - locating text regions in images",
37            },
38            TextRecognition {
39                output: $crate::domain::tasks::TextRecognitionOutput,
40                adapter: $crate::domain::adapters::TextRecognitionAdapter,
41                constructor: text_recognition,
42                conversion: into_text_recognition,
43                doc: "Text recognition - converting text regions to strings",
44            },
45            DocumentOrientation {
46                output: $crate::domain::tasks::DocumentOrientationOutput,
47                adapter: $crate::domain::adapters::DocumentOrientationAdapter,
48                constructor: document_orientation,
49                conversion: into_document_orientation,
50                doc: "Document orientation classification",
51            },
52            TextLineOrientation {
53                output: $crate::domain::tasks::TextLineOrientationOutput,
54                adapter: $crate::domain::adapters::TextLineOrientationAdapter,
55                constructor: text_line_orientation,
56                conversion: into_text_line_orientation,
57                doc: "Text line orientation classification",
58            },
59            DocumentRectification {
60                output: $crate::domain::tasks::DocumentRectificationOutput,
61                adapter: $crate::domain::adapters::UVDocRectifierAdapter,
62                constructor: document_rectification,
63                conversion: into_document_rectification,
64                doc: "Document rectification/unwarp",
65            },
66            LayoutDetection {
67                output: $crate::domain::tasks::LayoutDetectionOutput,
68                adapter: $crate::domain::adapters::LayoutDetectionAdapter,
69                constructor: layout_detection,
70                conversion: into_layout_detection,
71                doc: "Layout detection/analysis",
72            },
73            TableCellDetection {
74                output: $crate::domain::tasks::TableCellDetectionOutput,
75                adapter: $crate::domain::adapters::TableCellDetectionAdapter,
76                constructor: table_cell_detection,
77                conversion: into_table_cell_detection,
78                doc: "Table cell detection - locating cells within table regions",
79            },
80            FormulaRecognition {
81                output: $crate::domain::tasks::FormulaRecognitionOutput,
82                adapter: $crate::domain::adapters::FormulaRecognitionAdapter,
83                constructor: formula_recognition,
84                conversion: into_formula_recognition,
85                doc: "Formula recognition - converting mathematical formulas to LaTeX",
86            },
87            SealTextDetection {
88                output: $crate::domain::tasks::SealTextDetectionOutput,
89                adapter: $crate::domain::adapters::SealTextDetectionAdapter,
90                constructor: seal_text_detection,
91                conversion: into_seal_text_detection,
92                doc: "Seal text detection - locating text regions in seal/stamp images",
93            },
94            TableClassification {
95                output: $crate::domain::tasks::TableClassificationOutput,
96                adapter: $crate::domain::adapters::TableClassificationAdapter,
97                constructor: table_classification,
98                conversion: into_table_classification,
99                doc: "Table classification - classifying table images as wired or wireless",
100            },
101            TableStructureRecognition {
102                output: $crate::domain::tasks::TableStructureRecognitionOutput,
103                adapter: $crate::domain::adapters::TableStructureRecognitionAdapter,
104                constructor: table_structure_recognition,
105                conversion: into_table_structure_recognition,
106                doc: "Table structure recognition - recognizing table structure as HTML with bboxes",
107            }
108        }
109    };
110}
111
112/// Generates the TaskType enum from the task registry.
113///
114/// Uses `TaskDefinition::TASK_NAME` for runtime metadata.
115/// Uses `doc` field for `#[doc]` attributes on variants.
116#[macro_export]
117macro_rules! impl_task_type_enum {
118    ($(
119        $task:ident {
120            output: $output:ty,
121            adapter: $adapter:ty,
122            constructor: $constructor:ident,
123            conversion: $conversion:ident,
124            doc: $doc:literal,
125        }
126    ),* $(,)?) => {
127        /// Represents the type of OCR task being performed.
128        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
129        pub enum TaskType {
130            $(
131                #[doc = $doc]
132                $task,
133            )*
134        }
135
136        impl TaskType {
137            /// Returns a human-readable name for the task type.
138            pub fn name(&self) -> &'static str {
139                match self {
140                    $(TaskType::$task => <$output as $crate::core::traits::TaskDefinition>::TASK_NAME,)*
141                }
142            }
143        }
144    };
145}
146
147/// Macro to handle optional nested config initialization in builders.
148///
149/// This macro eliminates the repeated pattern of:
150/// ```text
151/// // if self.config.field.is_none() {
152/// //     self.config.field = Some(Type::new());
153/// // }
154/// ```
155///
156/// # Usage
157///
158/// ```text
159/// // Instead of:
160/// // if self.config.orientation.is_none() {
161/// //     self.config.orientation = Some(DocOrientationClassifierConfig::new());
162/// // }
163/// // if let Some(ref mut config) = self.config.orientation {
164/// //     config.confidence_threshold = Some(threshold);
165/// // }
166///
167/// // Use:
168/// // with_nested!(self.config.orientation, DocOrientationClassifierConfig, config => {
169/// //     config.confidence_threshold = Some(threshold);
170/// // });
171/// ```
172#[macro_export]
173macro_rules! with_nested {
174    ($field:expr, $type:ty, $var:ident => $body:block) => {
175        if $field.is_none() {
176            $field = Some(<$type>::new());
177        }
178        if let Some(ref mut $var) = $field {
179            $body
180        }
181    };
182}
183
184/// Macro to create pre-populated StageMetrics with common patterns.
185///
186/// This macro reduces duplication in metrics construction across stages.
187///
188/// # Usage
189///
190/// ```text
191/// // Instead of:
192/// // StageMetrics::new(success_count, failure_count)
193/// //     .with_processing_time(start_time.elapsed())
194/// //     .with_info("stage", "cropping")
195/// //     .with_info("batch_size", batch_size.to_string())
196/// //     .with_info("parallel", parallel.to_string())
197///
198/// // Use:
199/// // metrics!(success_count, failure_count, start_time; stage = "cropping", batch_size = batch_size, parallel = parallel)
200/// // Or without timing:
201/// // metrics!(success_count, failure_count; stage = "cropping", batch_size = batch_size)
202/// ```
203#[macro_export]
204macro_rules! metrics {
205    // With timing
206    ($success:expr, $failure:expr, $start_time:expr; $($key:ident = $value:expr),*) => {
207        {
208            let mut metrics = $crate::pipeline::stages::StageMetrics::new($success, $failure);
209            metrics = metrics.with_processing_time($start_time.elapsed());
210            $(
211                metrics = metrics.with_info(stringify!($key), $value.to_string());
212            )*
213            metrics
214        }
215    };
216    // Without timing
217    ($success:expr, $failure:expr; $($key:ident = $value:expr),*) => {
218        {
219            let mut metrics = $crate::pipeline::stages::StageMetrics::new($success, $failure);
220            $(
221                metrics = metrics.with_info(stringify!($key), $value.to_string());
222            )*
223            metrics
224        }
225    };
226}
227
228/// Comprehensive builder macro for generating common builder method patterns.
229///
230/// This macro generates multiple types of builder methods to reduce code duplication:
231/// 1. Simple setters for direct field assignment
232/// 2. Nested config setters using the `with_nested!` macro
233/// 3. Enable/disable methods for optional features
234/// 4. Dynamic batching configuration methods
235///
236/// # Usage
237///
238/// ```text
239/// // impl_complete_builder! {
240/// //     builder: MyBuilder,
241/// //     config_field: config,
242///
243/// //     // Simple setters
244/// //     simple_setters: {
245/// //         field_name: FieldType => "Documentation for the setter",
246/// //     },
247///
248/// //     // Nested config setters
249/// //     nested_setters: {
250/// //         config_path: ConfigType => {
251/// //             field_name: FieldType => "Documentation",
252/// //         },
253/// //     },
254///
255/// //     // Enable/disable methods
256/// //     enable_methods: {
257/// //         method_name => config_field: DefaultType => "Documentation",
258/// //     },
259/// // }
260/// ```
261#[macro_export]
262macro_rules! impl_complete_builder {
263    // Simple setters only
264    (
265        builder: $builder:ident,
266        config_field: $config_field:ident,
267        simple_setters: {
268            $($simple_field:ident: $simple_type:ty => $simple_doc:literal),* $(,)?
269        }
270    ) => {
271        impl $builder {
272            $(
273                #[doc = $simple_doc]
274                pub fn $simple_field(mut self, value: $simple_type) -> Self {
275                    self.$config_field.$simple_field = Some(value);
276                    self
277                }
278            )*
279        }
280    };
281
282    // Nested setters only
283    (
284        builder: $builder:ident,
285        config_field: $config_field:ident,
286        nested_setters: {
287            $($nested_path:ident: $nested_type:ty => {
288                $($nested_field:ident: $nested_field_type:ty => $nested_doc:literal),* $(,)?
289            }),* $(,)?
290        }
291    ) => {
292        impl $builder {
293            $($(
294                #[doc = $nested_doc]
295                pub fn $nested_field(mut self, value: $nested_field_type) -> Self {
296                    $crate::with_nested!(self.$config_field.$nested_path, $nested_type, config => {
297                        config.$nested_field = Some(value);
298                    });
299                    self
300                }
301            )*)*
302        }
303    };
304
305    // Enable methods only
306    (
307        builder: $builder:ident,
308        config_field: $config_field:ident,
309        enable_methods: {
310            $($enable_method:ident => $enable_field:ident: $enable_type:ty => $enable_doc:literal),* $(,)?
311        }
312    ) => {
313        impl $builder {
314            $(
315                #[doc = $enable_doc]
316                pub fn $enable_method(mut self) -> Self {
317                    self.$config_field.$enable_field = Some(<$enable_type>::default());
318                    self
319                }
320            )*
321        }
322    };
323}
324
325/// Macro to implement `new()` and `with_common()` for config structs with per-module defaults.
326#[macro_export]
327macro_rules! impl_config_new_and_with_common {
328    (
329        $Config:ident,
330        common_defaults: ($model_name_opt:expr, $batch_size_opt:expr),
331        fields: { $( $field:ident : $default_expr:expr ),* $(,)? }
332    ) => {
333        impl $Config {
334            /// Creates a new config instance with default values
335            pub fn new() -> Self {
336                Self {
337                    common: $crate::core::config::builder::ModelInferenceConfig::with_defaults(
338                        $model_name_opt, $batch_size_opt
339                    ),
340                    $( $field: $default_expr ),*
341                }
342            }
343            /// Creates a new config instance using provided common configuration
344            pub fn with_common(common: $crate::core::config::builder::ModelInferenceConfig) -> Self {
345                Self {
346                    common,
347                    $( $field: $default_expr ),*
348                }
349            }
350        }
351    };
352}
353
354/// Macro to implement common builder methods for structs with a `ModelInferenceConfig` field.
355#[macro_export]
356macro_rules! impl_common_builder_methods {
357    ($Builder:ident, $common_field:ident) => {
358        impl $Builder {
359            /// Sets the model path
360            pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
361                self.$common_field = self.$common_field.model_path(model_path);
362                self
363            }
364            /// Sets the model name
365            pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
366                self.$common_field = self.$common_field.model_name(model_name);
367                self
368            }
369            /// Sets the batch size
370            pub fn batch_size(mut self, batch_size: usize) -> Self {
371                self.$common_field = self.$common_field.batch_size(batch_size);
372                self
373            }
374            /// Enables or disables logging
375            pub fn enable_logging(mut self, enable: bool) -> Self {
376                self.$common_field = self.$common_field.enable_logging(enable);
377                self
378            }
379            /// Sets the ONNX Runtime session configuration
380            pub fn ort_session(
381                mut self,
382                config: $crate::core::config::onnx::OrtSessionConfig,
383            ) -> Self {
384                self.$common_field = self.$common_field.ort_session(config);
385                self
386            }
387        }
388    };
389}
390
391/// Macro to inject common builder methods into an existing `impl Builder` block.
392/// Use this inside `impl YourBuilder { ... }` and pass the field name that holds
393/// `ModelInferenceConfig` (e.g., `common`).
394#[macro_export]
395macro_rules! common_builder_methods {
396    ($common_field:ident) => {
397        /// Sets the model path
398        pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
399            self.$common_field = self.$common_field.model_path(model_path);
400            self
401        }
402        /// Sets the model name
403        pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
404            self.$common_field = self.$common_field.model_name(model_name);
405            self
406        }
407        /// Sets the batch size
408        pub fn batch_size(mut self, batch_size: usize) -> Self {
409            self.$common_field = self.$common_field.batch_size(batch_size);
410            self
411        }
412        /// Enables or disables logging
413        pub fn enable_logging(mut self, enable: bool) -> Self {
414            self.$common_field = self.$common_field.enable_logging(enable);
415            self
416        }
417        /// Sets the ONNX Runtime session configuration
418        pub fn ort_session(mut self, config: $crate::core::config::onnx::OrtSessionConfig) -> Self {
419            self.$common_field = self.$common_field.ort_session(config);
420            self
421        }
422    };
423}
424
425/// Internal helper macro that generates the common parts of adapter builders.
426///
427/// This macro generates:
428/// - Builder struct definition
429/// - `new()` constructor
430/// - `base_adapter_info()` method
431/// - Custom methods
432/// - `Default` trait implementation
433/// - `OrtConfigurable` trait implementation
434///
435/// It does NOT generate:
436/// - `with_config()` inherent method (added separately for non-override variants)
437/// - `AdapterBuilder` trait implementation (varies based on overrides)
438#[doc(hidden)]
439#[macro_export]
440macro_rules! __impl_adapter_builder_common {
441    (
442        builder_name: $Builder:ident,
443        adapter_name: $Adapter:ident,
444        config_type: $Config:ty,
445        adapter_type: $adapter_type_str:literal,
446        adapter_desc: $adapter_desc:literal,
447        task_type: $TaskType:ident,
448
449        fields: {
450            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
451            $(,)?
452        },
453
454        methods: {
455            $($method:item)*
456        }
457    ) => {
458        /// Builder for [$Adapter].
459        ///
460        #[doc = $adapter_desc]
461        #[derive(Debug)]
462        pub struct $Builder {
463            /// Common configuration shared across all adapters
464            config: $crate::domain::adapters::builder_config::AdapterBuilderConfig<$Config>,
465            $($field_vis $field_name : $field_ty),*
466        }
467
468        impl $Builder {
469            /// Creates a new builder with default configuration.
470            pub fn new() -> Self {
471                Self {
472                    config: $crate::domain::adapters::builder_config::AdapterBuilderConfig::default(),
473                    $($field_name : $field_default),*
474                }
475            }
476
477            /// Creates the base [`AdapterInfo`] for this adapter.
478            ///
479            /// This helper method constructs an [`AdapterInfo`] using the adapter's
480            /// type, task type, and description from the macro.
481            pub fn base_adapter_info() -> $crate::core::traits::adapter::AdapterInfo {
482                $crate::core::traits::adapter::AdapterInfo::new(
483                    $adapter_type_str,
484                    $crate::core::traits::task::TaskType::$TaskType,
485                    $adapter_desc,
486                )
487            }
488
489            // Custom methods provided by the user
490            $($method)*
491        }
492
493        impl Default for $Builder {
494            fn default() -> Self {
495                Self::new()
496            }
497        }
498
499        impl $crate::core::traits::OrtConfigurable for $Builder {
500            fn with_ort_config(mut self, config: $crate::core::config::OrtSessionConfig) -> Self {
501                self.config = self.config.with_ort_config(config);
502                self
503            }
504        }
505    };
506}
507
508/// Macro to implement common adapter builder boilerplate.
509///
510/// This macro generates the repetitive parts of adapter builders including the `build()` method.
511/// Uses a callback pattern to work around Rust macro hygiene limitations.
512///
513/// Generates:
514/// - Builder struct with `config` field plus custom fields
515/// - `new()` constructor
516/// - `with_config()` convenience method
517/// - `Default` trait implementation
518/// - `OrtConfigurable` trait implementation
519/// - `AdapterBuilder` trait implementation (with callback-based `build()`)
520///
521/// # Syntax
522///
523/// ```rust,ignore
524/// impl_adapter_builder! {
525///     // Required: Type information
526///     builder_name: MyAdapterBuilder,
527///     adapter_name: MyAdapter,
528///     config_type: MyConfig,
529///     adapter_type: "MyAdapter",
530///     adapter_desc: "Description",
531///     task_type: MyTaskType,
532///
533///     // Optional: Custom fields
534///     fields: {
535///         pub custom_field: Option<String> = None,
536///     },
537///
538///     // Optional: Custom methods
539///     methods: {
540///         pub fn custom_method(mut self, value: String) -> Self {
541///             self.custom_field = Some(value);
542///             self
543///         }
544///     }
545///
546///     // Required: Build closure (use |builder, model_path| { ... })
547///     build: |builder, model_path| {
548///         let (task_config, ort_config) = builder.config.into_validated_parts()?;
549///         let model = apply_ort_config!(
550///             SomeModelBuilder::new(),
551///             ort_config
552///         ).build(model_path)?;
553///         Ok(MyAdapter::new(model, task_config))
554///     }
555/// }
556/// ```
557#[macro_export]
558macro_rules! impl_adapter_builder {
559    // Full variant with fields and methods (no overrides)
560    (
561        builder_name: $Builder:ident,
562        adapter_name: $Adapter:ident,
563        config_type: $Config:ty,
564        adapter_type: $adapter_type_str:literal,
565        adapter_desc: $adapter_desc:literal,
566        task_type: $TaskType:ident,
567
568        fields: {
569            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
570            $(,)?
571        },
572
573        methods: {
574            $($method:item)*
575        }
576
577        build: $build_closure:expr,
578    ) => {
579        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
580        $crate::__impl_adapter_builder_common! {
581            builder_name: $Builder,
582            adapter_name: $Adapter,
583            config_type: $Config,
584            adapter_type: $adapter_type_str,
585            adapter_desc: $adapter_desc,
586            task_type: $TaskType,
587
588            fields: {
589                $($field_vis $field_name : $field_ty = $field_default),*
590            },
591
592            methods: {
593                /// Sets the task configuration.
594                pub fn with_config(mut self, config: $Config) -> Self {
595                    self.config = self.config.with_task_config(config);
596                    self
597                }
598
599                $($method)*
600            }
601        }
602
603        // Generate AdapterBuilder impl with standard methods
604        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
605            type Config = $Config;
606            type Adapter = $Adapter;
607
608            fn build(
609                self,
610                model_source: impl Into<$crate::core::ModelSource>,
611            ) -> Result<Self::Adapter, $crate::core::OCRError> {
612                let build_fn: fn(Self, $crate::core::ModelSource) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
613                build_fn(self, model_source.into())
614            }
615
616            fn with_config(mut self, config: Self::Config) -> Self {
617                self.config = self.config.with_task_config(config);
618                self
619            }
620
621            fn adapter_type(&self) -> &str {
622                $adapter_type_str
623            }
624        }
625    };
626
627    // Variant without custom fields
628    (
629        builder_name: $Builder:ident,
630        adapter_name: $Adapter:ident,
631        config_type: $Config:ty,
632        adapter_type: $adapter_type_str:literal,
633        adapter_desc: $adapter_desc:literal,
634        task_type: $TaskType:ident,
635
636        methods: {
637            $($method:item)*
638        }
639
640        build: $build_closure:expr,
641    ) => {
642        impl_adapter_builder! {
643            builder_name: $Builder,
644            adapter_name: $Adapter,
645            config_type: $Config,
646            adapter_type: $adapter_type_str,
647            adapter_desc: $adapter_desc,
648            task_type: $TaskType,
649
650            fields: {},
651
652            methods: {
653                $($method)*
654            }
655
656            build: $build_closure,
657        }
658    };
659
660    // Variant without custom methods
661    (
662        builder_name: $Builder:ident,
663        adapter_name: $Adapter:ident,
664        config_type: $Config:ty,
665        adapter_type: $adapter_type_str:literal,
666        adapter_desc: $adapter_desc:literal,
667        task_type: $TaskType:ident,
668
669        fields: {
670            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
671            $(,)?
672        }
673
674        build: $build_closure:expr,
675    ) => {
676        impl_adapter_builder! {
677            builder_name: $Builder,
678            adapter_name: $Adapter,
679            config_type: $Config,
680            adapter_type: $adapter_type_str,
681            adapter_desc: $adapter_desc,
682            task_type: $TaskType,
683
684            fields: {
685                $($field_vis $field_name : $field_ty = $field_default),*
686            },
687
688            methods: {}
689
690            build: $build_closure,
691        }
692    };
693
694    // Minimal variant (no custom fields, no custom methods)
695    (
696        builder_name: $Builder:ident,
697        adapter_name: $Adapter:ident,
698        config_type: $Config:ty,
699        adapter_type: $adapter_type_str:literal,
700        adapter_desc: $adapter_desc:literal,
701        task_type: $TaskType:ident
702
703        build: $build_closure:expr,
704    ) => {
705        impl_adapter_builder! {
706            builder_name: $Builder,
707            adapter_name: $Adapter,
708            config_type: $Config,
709            adapter_type: $adapter_type_str,
710            adapter_desc: $adapter_desc,
711            task_type: $TaskType,
712
713            fields: {},
714
715            methods: {}
716
717            build: $build_closure,
718        }
719    };
720
721    // Variant with trait method overrides (for with_config, adapter_type)
722    (
723        builder_name: $Builder:ident,
724        adapter_name: $Adapter:ident,
725        config_type: $Config:ty,
726        adapter_type: $adapter_type_str:literal,
727        adapter_desc: $adapter_desc:literal,
728        task_type: $TaskType:ident,
729
730        fields: {
731            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
732            $(,)?
733        },
734
735        methods: {
736            $($method:item)*
737        }
738
739        overrides: {
740            with_config: $with_config_closure:expr,
741            adapter_type: $adapter_type_closure:expr,
742        }
743
744        build: $build_closure:expr,
745    ) => {
746        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
747        $crate::__impl_adapter_builder_common! {
748            builder_name: $Builder,
749            adapter_name: $Adapter,
750            config_type: $Config,
751            adapter_type: $adapter_type_str,
752            adapter_desc: $adapter_desc,
753            task_type: $TaskType,
754
755            fields: {
756                $($field_vis $field_name : $field_ty = $field_default),*
757            },
758
759            methods: {
760                $($method)*
761            }
762        }
763
764        // Generate AdapterBuilder impl with overridden methods
765        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
766            type Config = $Config;
767            type Adapter = $Adapter;
768
769            fn build(
770                self,
771                model_source: impl Into<$crate::core::ModelSource>,
772            ) -> Result<Self::Adapter, $crate::core::OCRError> {
773                let build_fn: fn(Self, $crate::core::ModelSource) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
774                build_fn(self, model_source.into())
775            }
776
777            fn with_config(self, config: Self::Config) -> Self {
778                let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
779                with_config_fn(self, config)
780            }
781
782            fn adapter_type(&self) -> &str {
783                let adapter_type_fn: fn(&Self) -> &str = $adapter_type_closure;
784                adapter_type_fn(self)
785            }
786        }
787    };
788
789    // Variant with only with_config override
790    (
791        builder_name: $Builder:ident,
792        adapter_name: $Adapter:ident,
793        config_type: $Config:ty,
794        adapter_type: $adapter_type_str:literal,
795        adapter_desc: $adapter_desc:literal,
796        task_type: $TaskType:ident,
797
798        fields: {
799            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
800            $(,)?
801        },
802
803        methods: {
804            $($method:item)*
805        }
806
807        overrides: {
808            with_config: $with_config_closure:expr,
809        }
810
811        build: $build_closure:expr,
812    ) => {
813        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
814        $crate::__impl_adapter_builder_common! {
815            builder_name: $Builder,
816            adapter_name: $Adapter,
817            config_type: $Config,
818            adapter_type: $adapter_type_str,
819            adapter_desc: $adapter_desc,
820            task_type: $TaskType,
821
822            fields: {
823                $($field_vis $field_name : $field_ty = $field_default),*
824            },
825
826            methods: {
827                $($method)*
828            }
829        }
830
831        // Generate AdapterBuilder impl with with_config override
832        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
833            type Config = $Config;
834            type Adapter = $Adapter;
835
836            fn build(
837                self,
838                model_source: impl Into<$crate::core::ModelSource>,
839            ) -> Result<Self::Adapter, $crate::core::OCRError> {
840                let build_fn: fn(Self, $crate::core::ModelSource) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
841                build_fn(self, model_source.into())
842            }
843
844            fn with_config(self, config: Self::Config) -> Self {
845                let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
846                with_config_fn(self, config)
847            }
848
849            fn adapter_type(&self) -> &str {
850                $adapter_type_str
851            }
852        }
853    };
854}
855
856/// Macro to conditionally apply OrtSessionConfig to any builder that has `with_ort_config`.
857///
858/// This macro eliminates the repeated pattern:
859/// ```text
860/// // let mut builder = SomeBuilder::new();
861/// // if let Some(ort_config) = ort_config {
862/// //     builder = builder.with_ort_config(ort_config);
863/// // }
864/// ```
865///
866/// Instead, use:
867/// ```text
868/// // let builder = apply_ort_config!(SomeBuilder::new(), ort_config);
869/// ```
870///
871/// # Usage
872///
873/// ```text
874/// // Works with any builder that has a `with_ort_config` method:
875/// // let builder = apply_ort_config!(
876/// //     DBModelBuilder::new()
877/// //         .preprocess_config(config),
878/// //     ort_config
879/// // );
880/// ```
881#[macro_export]
882macro_rules! apply_ort_config {
883    ($builder:expr, $ort_config:expr) => {{
884        let builder = $builder;
885        if let Some(cfg) = $ort_config {
886            builder.with_ort_config(cfg)
887        } else {
888            builder
889        }
890    }};
891}
892
893#[cfg(test)]
894mod tests {
895
896    // Test configuration structs
897    #[derive(Debug, Default)]
898    struct TestConfig {
899        simple_field: Option<String>,
900        nested_config: Option<NestedConfig>,
901        enable_field: Option<EnabledFeature>,
902    }
903
904    #[derive(Debug, Default)]
905    struct NestedConfig {
906        nested_field: Option<i32>,
907    }
908
909    impl NestedConfig {
910        fn new() -> Self {
911            Self::default()
912        }
913    }
914
915    #[derive(Debug, Default)]
916    struct EnabledFeature {
917        _enabled: bool,
918    }
919
920    // Test builder struct
921    #[derive(Debug)]
922    struct TestBuilder {
923        config: TestConfig,
924    }
925
926    impl TestBuilder {
927        fn new() -> Self {
928            Self {
929                config: TestConfig::default(),
930            }
931        }
932
933        fn get_config(&self) -> &TestConfig {
934            &self.config
935        }
936    }
937
938    // Apply the macro to generate builder methods (separate calls for each type)
939    impl_complete_builder! {
940        builder: TestBuilder,
941        config_field: config,
942        simple_setters: {
943            simple_field: String => "Sets a simple field value",
944        }
945    }
946
947    impl_complete_builder! {
948        builder: TestBuilder,
949        config_field: config,
950        nested_setters: {
951            nested_config: NestedConfig => {
952                nested_field: i32 => "Sets a nested field value",
953            },
954        }
955    }
956
957    impl_complete_builder! {
958        builder: TestBuilder,
959        config_field: config,
960        enable_methods: {
961            enable_feature => enable_field: EnabledFeature => "Enables a feature with default configuration",
962        }
963    }
964
965    #[test]
966    fn test_impl_complete_builder_nested_setter() {
967        let builder = TestBuilder::new().nested_field(42);
968
969        assert!(builder.get_config().nested_config.is_some());
970        let Some(nested) = builder.get_config().nested_config.as_ref() else {
971            panic!("expected nested_config to be Some");
972        };
973        assert_eq!(nested.nested_field, Some(42));
974    }
975
976    #[test]
977    fn test_impl_complete_builder_enable_method() {
978        let builder = TestBuilder::new().enable_feature();
979
980        assert!(builder.get_config().enable_field.is_some());
981    }
982
983    #[test]
984    fn test_impl_complete_builder_chaining() {
985        let builder = TestBuilder::new()
986            .simple_field("test".to_string())
987            .nested_field(123)
988            .enable_feature();
989
990        let config = builder.get_config();
991        assert_eq!(config.simple_field, Some("test".to_string()));
992        assert!(config.nested_config.is_some());
993        let Some(nested) = config.nested_config.as_ref() else {
994            panic!("expected nested_config to be Some");
995        };
996        assert_eq!(nested.nested_field, Some(123));
997        assert!(config.enable_field.is_some());
998    }
999}