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/// Comprehensive builder macro for generating common builder method patterns.
185///
186/// This macro generates multiple types of builder methods to reduce code duplication:
187/// 1. Simple setters for direct field assignment
188/// 2. Nested config setters using the `with_nested!` macro
189/// 3. Enable/disable methods for optional features
190/// 4. Dynamic batching configuration methods
191///
192/// # Usage
193///
194/// ```text
195/// // impl_complete_builder! {
196/// //     builder: MyBuilder,
197/// //     config_field: config,
198///
199/// //     // Simple setters
200/// //     simple_setters: {
201/// //         field_name: FieldType => "Documentation for the setter",
202/// //     },
203///
204/// //     // Nested config setters
205/// //     nested_setters: {
206/// //         config_path: ConfigType => {
207/// //             field_name: FieldType => "Documentation",
208/// //         },
209/// //     },
210///
211/// //     // Enable/disable methods
212/// //     enable_methods: {
213/// //         method_name => config_field: DefaultType => "Documentation",
214/// //     },
215/// // }
216/// ```
217#[macro_export]
218macro_rules! impl_complete_builder {
219    // Simple setters only
220    (
221        builder: $builder:ident,
222        config_field: $config_field:ident,
223        simple_setters: {
224            $($simple_field:ident: $simple_type:ty => $simple_doc:literal),* $(,)?
225        }
226    ) => {
227        impl $builder {
228            $(
229                #[doc = $simple_doc]
230                pub fn $simple_field(mut self, value: $simple_type) -> Self {
231                    self.$config_field.$simple_field = Some(value);
232                    self
233                }
234            )*
235        }
236    };
237
238    // Nested setters only
239    (
240        builder: $builder:ident,
241        config_field: $config_field:ident,
242        nested_setters: {
243            $($nested_path:ident: $nested_type:ty => {
244                $($nested_field:ident: $nested_field_type:ty => $nested_doc:literal),* $(,)?
245            }),* $(,)?
246        }
247    ) => {
248        impl $builder {
249            $($(
250                #[doc = $nested_doc]
251                pub fn $nested_field(mut self, value: $nested_field_type) -> Self {
252                    $crate::with_nested!(self.$config_field.$nested_path, $nested_type, config => {
253                        config.$nested_field = Some(value);
254                    });
255                    self
256                }
257            )*)*
258        }
259    };
260
261    // Enable methods only
262    (
263        builder: $builder:ident,
264        config_field: $config_field:ident,
265        enable_methods: {
266            $($enable_method:ident => $enable_field:ident: $enable_type:ty => $enable_doc:literal),* $(,)?
267        }
268    ) => {
269        impl $builder {
270            $(
271                #[doc = $enable_doc]
272                pub fn $enable_method(mut self) -> Self {
273                    self.$config_field.$enable_field = Some(<$enable_type>::default());
274                    self
275                }
276            )*
277        }
278    };
279}
280
281/// Macro to implement `new()` and `with_common()` for config structs with per-module defaults.
282#[macro_export]
283macro_rules! impl_config_new_and_with_common {
284    (
285        $Config:ident,
286        common_defaults: ($model_name_opt:expr, $batch_size_opt:expr),
287        fields: { $( $field:ident : $default_expr:expr ),* $(,)? }
288    ) => {
289        impl $Config {
290            /// Creates a new config instance with default values
291            pub fn new() -> Self {
292                Self {
293                    common: $crate::core::config::builder::ModelInferenceConfig::with_defaults(
294                        $model_name_opt, $batch_size_opt
295                    ),
296                    $( $field: $default_expr ),*
297                }
298            }
299            /// Creates a new config instance using provided common configuration
300            pub fn with_common(common: $crate::core::config::builder::ModelInferenceConfig) -> Self {
301                Self {
302                    common,
303                    $( $field: $default_expr ),*
304                }
305            }
306        }
307    };
308}
309
310/// Macro to implement common builder methods for structs with a `ModelInferenceConfig` field.
311#[macro_export]
312macro_rules! impl_common_builder_methods {
313    ($Builder:ident, $common_field:ident) => {
314        impl $Builder {
315            /// Sets the model path
316            pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
317                self.$common_field = self.$common_field.model_path(model_path);
318                self
319            }
320            /// Sets the model name
321            pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
322                self.$common_field = self.$common_field.model_name(model_name);
323                self
324            }
325            /// Sets the batch size
326            pub fn batch_size(mut self, batch_size: usize) -> Self {
327                self.$common_field = self.$common_field.batch_size(batch_size);
328                self
329            }
330            /// Enables or disables logging
331            pub fn enable_logging(mut self, enable: bool) -> Self {
332                self.$common_field = self.$common_field.enable_logging(enable);
333                self
334            }
335            /// Sets the ONNX Runtime session configuration
336            pub fn ort_session(
337                mut self,
338                config: $crate::core::config::onnx::OrtSessionConfig,
339            ) -> Self {
340                self.$common_field = self.$common_field.ort_session(config);
341                self
342            }
343        }
344    };
345}
346
347/// Macro to inject common builder methods into an existing `impl Builder` block.
348/// Use this inside `impl YourBuilder { ... }` and pass the field name that holds
349/// `ModelInferenceConfig` (e.g., `common`).
350#[macro_export]
351macro_rules! common_builder_methods {
352    ($common_field:ident) => {
353        /// Sets the model path
354        pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
355            self.$common_field = self.$common_field.model_path(model_path);
356            self
357        }
358        /// Sets the model name
359        pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
360            self.$common_field = self.$common_field.model_name(model_name);
361            self
362        }
363        /// Sets the batch size
364        pub fn batch_size(mut self, batch_size: usize) -> Self {
365            self.$common_field = self.$common_field.batch_size(batch_size);
366            self
367        }
368        /// Enables or disables logging
369        pub fn enable_logging(mut self, enable: bool) -> Self {
370            self.$common_field = self.$common_field.enable_logging(enable);
371            self
372        }
373        /// Sets the ONNX Runtime session configuration
374        pub fn ort_session(mut self, config: $crate::core::config::onnx::OrtSessionConfig) -> Self {
375            self.$common_field = self.$common_field.ort_session(config);
376            self
377        }
378    };
379}
380
381/// Internal helper macro that generates the common parts of adapter builders.
382///
383/// This macro generates:
384/// - Builder struct definition
385/// - `new()` constructor
386/// - `base_adapter_info()` method
387/// - Custom methods
388/// - `Default` trait implementation
389/// - `OrtConfigurable` trait implementation
390///
391/// It does NOT generate:
392/// - `with_config()` inherent method (added separately for non-override variants)
393/// - `AdapterBuilder` trait implementation (varies based on overrides)
394#[doc(hidden)]
395#[macro_export]
396macro_rules! __impl_adapter_builder_common {
397    (
398        builder_name: $Builder:ident,
399        adapter_name: $Adapter:ident,
400        config_type: $Config:ty,
401        adapter_type: $adapter_type_str:literal,
402        adapter_desc: $adapter_desc:literal,
403        task_type: $TaskType:ident,
404
405        fields: {
406            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
407            $(,)?
408        },
409
410        methods: {
411            $($method:item)*
412        }
413    ) => {
414        /// Builder for [$Adapter].
415        ///
416        #[doc = $adapter_desc]
417        #[derive(Debug)]
418        pub struct $Builder {
419            /// Common configuration shared across all adapters
420            config: $crate::domain::adapters::builder_config::AdapterBuilderConfig<$Config>,
421            $($field_vis $field_name : $field_ty),*
422        }
423
424        impl $Builder {
425            /// Creates a new builder with default configuration.
426            pub fn new() -> Self {
427                Self {
428                    config: $crate::domain::adapters::builder_config::AdapterBuilderConfig::default(),
429                    $($field_name : $field_default),*
430                }
431            }
432
433            /// Creates the base [`AdapterInfo`] for this adapter.
434            ///
435            /// This helper method constructs an [`AdapterInfo`] using the adapter's
436            /// type, task type, and description from the macro.
437            pub fn base_adapter_info() -> $crate::core::traits::adapter::AdapterInfo {
438                $crate::core::traits::adapter::AdapterInfo::new(
439                    $adapter_type_str,
440                    $crate::core::traits::task::TaskType::$TaskType,
441                    $adapter_desc,
442                )
443            }
444
445            // Custom methods provided by the user
446            $($method)*
447        }
448
449        impl Default for $Builder {
450            fn default() -> Self {
451                Self::new()
452            }
453        }
454
455        impl $crate::core::traits::OrtConfigurable for $Builder {
456            fn with_ort_config(mut self, config: $crate::core::config::OrtSessionConfig) -> Self {
457                self.config = self.config.with_ort_config(config);
458                self
459            }
460        }
461    };
462}
463
464/// Macro to implement common adapter builder boilerplate.
465///
466/// This macro generates the repetitive parts of adapter builders including the `build()` method.
467/// Uses a callback pattern to work around Rust macro hygiene limitations.
468///
469/// Generates:
470/// - Builder struct with `config` field plus custom fields
471/// - `new()` constructor
472/// - `with_config()` convenience method
473/// - `Default` trait implementation
474/// - `OrtConfigurable` trait implementation
475/// - `AdapterBuilder` trait implementation (with callback-based `build()`)
476///
477/// # Syntax
478///
479/// ```rust,ignore
480/// impl_adapter_builder! {
481///     // Required: Type information
482///     builder_name: MyAdapterBuilder,
483///     adapter_name: MyAdapter,
484///     config_type: MyConfig,
485///     adapter_type: "MyAdapter",
486///     adapter_desc: "Description",
487///     task_type: MyTaskType,
488///
489///     // Optional: Custom fields
490///     fields: {
491///         pub custom_field: Option<String> = None,
492///     },
493///
494///     // Optional: Custom methods
495///     methods: {
496///         pub fn custom_method(mut self, value: String) -> Self {
497///             self.custom_field = Some(value);
498///             self
499///         }
500///     }
501///
502///     // Required: Build closure (use |builder, model_path| { ... })
503///     build: |builder, model_path| {
504///         let (task_config, ort_config) = builder.config.into_validated_parts()?;
505///         let model = apply_ort_config!(
506///             SomeModelBuilder::new(),
507///             ort_config
508///         ).build(model_path)?;
509///         Ok(MyAdapter::new(model, task_config))
510///     }
511/// }
512/// ```
513#[macro_export]
514macro_rules! impl_adapter_builder {
515    // Full variant with fields and methods (no overrides)
516    (
517        builder_name: $Builder:ident,
518        adapter_name: $Adapter:ident,
519        config_type: $Config:ty,
520        adapter_type: $adapter_type_str:literal,
521        adapter_desc: $adapter_desc:literal,
522        task_type: $TaskType:ident,
523
524        fields: {
525            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
526            $(,)?
527        },
528
529        methods: {
530            $($method:item)*
531        }
532
533        build: $build_closure:expr,
534    ) => {
535        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
536        $crate::__impl_adapter_builder_common! {
537            builder_name: $Builder,
538            adapter_name: $Adapter,
539            config_type: $Config,
540            adapter_type: $adapter_type_str,
541            adapter_desc: $adapter_desc,
542            task_type: $TaskType,
543
544            fields: {
545                $($field_vis $field_name : $field_ty = $field_default),*
546            },
547
548            methods: {
549                /// Sets the task configuration.
550                pub fn with_config(mut self, config: $Config) -> Self {
551                    self.config = self.config.with_task_config(config);
552                    self
553                }
554
555                $($method)*
556            }
557        }
558
559        // Generate AdapterBuilder impl with standard methods
560        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
561            type Config = $Config;
562            type Adapter = $Adapter;
563
564            fn build(
565                self,
566                model_source: impl Into<$crate::core::ModelSource>,
567            ) -> Result<Self::Adapter, $crate::core::OCRError> {
568                let build_fn: fn(Self, $crate::core::ModelSource) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
569                build_fn(self, model_source.into())
570            }
571
572            fn with_config(mut self, config: Self::Config) -> Self {
573                self.config = self.config.with_task_config(config);
574                self
575            }
576
577            fn adapter_type(&self) -> &str {
578                $adapter_type_str
579            }
580        }
581    };
582
583    // Variant without custom fields
584    (
585        builder_name: $Builder:ident,
586        adapter_name: $Adapter:ident,
587        config_type: $Config:ty,
588        adapter_type: $adapter_type_str:literal,
589        adapter_desc: $adapter_desc:literal,
590        task_type: $TaskType:ident,
591
592        methods: {
593            $($method:item)*
594        }
595
596        build: $build_closure:expr,
597    ) => {
598        impl_adapter_builder! {
599            builder_name: $Builder,
600            adapter_name: $Adapter,
601            config_type: $Config,
602            adapter_type: $adapter_type_str,
603            adapter_desc: $adapter_desc,
604            task_type: $TaskType,
605
606            fields: {},
607
608            methods: {
609                $($method)*
610            }
611
612            build: $build_closure,
613        }
614    };
615
616    // Variant without custom methods
617    (
618        builder_name: $Builder:ident,
619        adapter_name: $Adapter:ident,
620        config_type: $Config:ty,
621        adapter_type: $adapter_type_str:literal,
622        adapter_desc: $adapter_desc:literal,
623        task_type: $TaskType:ident,
624
625        fields: {
626            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
627            $(,)?
628        }
629
630        build: $build_closure:expr,
631    ) => {
632        impl_adapter_builder! {
633            builder_name: $Builder,
634            adapter_name: $Adapter,
635            config_type: $Config,
636            adapter_type: $adapter_type_str,
637            adapter_desc: $adapter_desc,
638            task_type: $TaskType,
639
640            fields: {
641                $($field_vis $field_name : $field_ty = $field_default),*
642            },
643
644            methods: {}
645
646            build: $build_closure,
647        }
648    };
649
650    // Minimal variant (no custom fields, no custom methods)
651    (
652        builder_name: $Builder:ident,
653        adapter_name: $Adapter:ident,
654        config_type: $Config:ty,
655        adapter_type: $adapter_type_str:literal,
656        adapter_desc: $adapter_desc:literal,
657        task_type: $TaskType:ident
658
659        build: $build_closure:expr,
660    ) => {
661        impl_adapter_builder! {
662            builder_name: $Builder,
663            adapter_name: $Adapter,
664            config_type: $Config,
665            adapter_type: $adapter_type_str,
666            adapter_desc: $adapter_desc,
667            task_type: $TaskType,
668
669            fields: {},
670
671            methods: {}
672
673            build: $build_closure,
674        }
675    };
676
677    // Variant with trait method overrides (for with_config, adapter_type)
678    (
679        builder_name: $Builder:ident,
680        adapter_name: $Adapter:ident,
681        config_type: $Config:ty,
682        adapter_type: $adapter_type_str:literal,
683        adapter_desc: $adapter_desc:literal,
684        task_type: $TaskType:ident,
685
686        fields: {
687            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
688            $(,)?
689        },
690
691        methods: {
692            $($method:item)*
693        }
694
695        overrides: {
696            with_config: $with_config_closure:expr,
697            adapter_type: $adapter_type_closure:expr,
698        }
699
700        build: $build_closure:expr,
701    ) => {
702        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
703        $crate::__impl_adapter_builder_common! {
704            builder_name: $Builder,
705            adapter_name: $Adapter,
706            config_type: $Config,
707            adapter_type: $adapter_type_str,
708            adapter_desc: $adapter_desc,
709            task_type: $TaskType,
710
711            fields: {
712                $($field_vis $field_name : $field_ty = $field_default),*
713            },
714
715            methods: {
716                $($method)*
717            }
718        }
719
720        // Generate AdapterBuilder impl with overridden methods
721        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
722            type Config = $Config;
723            type Adapter = $Adapter;
724
725            fn build(
726                self,
727                model_source: impl Into<$crate::core::ModelSource>,
728            ) -> Result<Self::Adapter, $crate::core::OCRError> {
729                let build_fn: fn(Self, $crate::core::ModelSource) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
730                build_fn(self, model_source.into())
731            }
732
733            fn with_config(self, config: Self::Config) -> Self {
734                let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
735                with_config_fn(self, config)
736            }
737
738            fn adapter_type(&self) -> &str {
739                let adapter_type_fn: fn(&Self) -> &str = $adapter_type_closure;
740                adapter_type_fn(self)
741            }
742        }
743    };
744
745    // Variant with only with_config override
746    (
747        builder_name: $Builder:ident,
748        adapter_name: $Adapter:ident,
749        config_type: $Config:ty,
750        adapter_type: $adapter_type_str:literal,
751        adapter_desc: $adapter_desc:literal,
752        task_type: $TaskType:ident,
753
754        fields: {
755            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
756            $(,)?
757        },
758
759        methods: {
760            $($method:item)*
761        }
762
763        overrides: {
764            with_config: $with_config_closure:expr,
765        }
766
767        build: $build_closure:expr,
768    ) => {
769        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
770        $crate::__impl_adapter_builder_common! {
771            builder_name: $Builder,
772            adapter_name: $Adapter,
773            config_type: $Config,
774            adapter_type: $adapter_type_str,
775            adapter_desc: $adapter_desc,
776            task_type: $TaskType,
777
778            fields: {
779                $($field_vis $field_name : $field_ty = $field_default),*
780            },
781
782            methods: {
783                $($method)*
784            }
785        }
786
787        // Generate AdapterBuilder impl with with_config override
788        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
789            type Config = $Config;
790            type Adapter = $Adapter;
791
792            fn build(
793                self,
794                model_source: impl Into<$crate::core::ModelSource>,
795            ) -> Result<Self::Adapter, $crate::core::OCRError> {
796                let build_fn: fn(Self, $crate::core::ModelSource) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
797                build_fn(self, model_source.into())
798            }
799
800            fn with_config(self, config: Self::Config) -> Self {
801                let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
802                with_config_fn(self, config)
803            }
804
805            fn adapter_type(&self) -> &str {
806                $adapter_type_str
807            }
808        }
809    };
810}
811
812/// Macro to conditionally apply OrtSessionConfig to any builder that has `with_ort_config`.
813///
814/// This macro eliminates the repeated pattern:
815/// ```text
816/// // let mut builder = SomeBuilder::new();
817/// // if let Some(ort_config) = ort_config {
818/// //     builder = builder.with_ort_config(ort_config);
819/// // }
820/// ```
821///
822/// Instead, use:
823/// ```text
824/// // let builder = apply_ort_config!(SomeBuilder::new(), ort_config);
825/// ```
826///
827/// # Usage
828///
829/// ```text
830/// // Works with any builder that has a `with_ort_config` method:
831/// // let builder = apply_ort_config!(
832/// //     DBModelBuilder::new()
833/// //         .preprocess_config(config),
834/// //     ort_config
835/// // );
836/// ```
837#[macro_export]
838macro_rules! apply_ort_config {
839    ($builder:expr, $ort_config:expr) => {{
840        let builder = $builder;
841        if let Some(cfg) = $ort_config {
842            builder.with_ort_config(cfg)
843        } else {
844            builder
845        }
846    }};
847}
848
849#[cfg(test)]
850mod tests {
851
852    // Test configuration structs
853    #[derive(Debug, Default)]
854    struct TestConfig {
855        simple_field: Option<String>,
856        nested_config: Option<NestedConfig>,
857        enable_field: Option<EnabledFeature>,
858    }
859
860    #[derive(Debug, Default)]
861    struct NestedConfig {
862        nested_field: Option<i32>,
863    }
864
865    impl NestedConfig {
866        fn new() -> Self {
867            Self::default()
868        }
869    }
870
871    #[derive(Debug, Default)]
872    struct EnabledFeature {
873        _enabled: bool,
874    }
875
876    // Test builder struct
877    #[derive(Debug)]
878    struct TestBuilder {
879        config: TestConfig,
880    }
881
882    impl TestBuilder {
883        fn new() -> Self {
884            Self {
885                config: TestConfig::default(),
886            }
887        }
888
889        fn get_config(&self) -> &TestConfig {
890            &self.config
891        }
892    }
893
894    // Apply the macro to generate builder methods (separate calls for each type)
895    impl_complete_builder! {
896        builder: TestBuilder,
897        config_field: config,
898        simple_setters: {
899            simple_field: String => "Sets a simple field value",
900        }
901    }
902
903    impl_complete_builder! {
904        builder: TestBuilder,
905        config_field: config,
906        nested_setters: {
907            nested_config: NestedConfig => {
908                nested_field: i32 => "Sets a nested field value",
909            },
910        }
911    }
912
913    impl_complete_builder! {
914        builder: TestBuilder,
915        config_field: config,
916        enable_methods: {
917            enable_feature => enable_field: EnabledFeature => "Enables a feature with default configuration",
918        }
919    }
920
921    #[test]
922    fn test_impl_complete_builder_nested_setter() {
923        let builder = TestBuilder::new().nested_field(42);
924
925        assert!(builder.get_config().nested_config.is_some());
926        let Some(nested) = builder.get_config().nested_config.as_ref() else {
927            panic!("expected nested_config to be Some");
928        };
929        assert_eq!(nested.nested_field, Some(42));
930    }
931
932    #[test]
933    fn test_impl_complete_builder_enable_method() {
934        let builder = TestBuilder::new().enable_feature();
935
936        assert!(builder.get_config().enable_field.is_some());
937    }
938
939    #[test]
940    fn test_impl_complete_builder_chaining() {
941        let builder = TestBuilder::new()
942            .simple_field("test".to_string())
943            .nested_field(123)
944            .enable_feature();
945
946        let config = builder.get_config();
947        assert_eq!(config.simple_field, Some("test".to_string()));
948        assert!(config.nested_config.is_some());
949        let Some(nested) = config.nested_config.as_ref() else {
950            panic!("expected nested_config to be Some");
951        };
952        assert_eq!(nested.nested_field, Some(123));
953        assert!(config.enable_field.is_some());
954    }
955}