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/// ```rust,no_run
151/// // if self.config.field.is_none() {
152/// //     self.config.field = Some(Type::new());
153/// // }
154/// ```
155///
156/// # Usage
157///
158/// ```rust,no_run
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/// ```rust,no_run
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/// ```rust,no_run
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(self, model_path: &std::path::Path) -> Result<Self::Adapter, $crate::core::OCRError> {
609                let build_fn: fn(Self, &std::path::Path) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
610                build_fn(self, model_path)
611            }
612
613            fn with_config(mut self, config: Self::Config) -> Self {
614                self.config = self.config.with_task_config(config);
615                self
616            }
617
618            fn adapter_type(&self) -> &str {
619                $adapter_type_str
620            }
621        }
622    };
623
624    // Variant without custom fields
625    (
626        builder_name: $Builder:ident,
627        adapter_name: $Adapter:ident,
628        config_type: $Config:ty,
629        adapter_type: $adapter_type_str:literal,
630        adapter_desc: $adapter_desc:literal,
631        task_type: $TaskType:ident,
632
633        methods: {
634            $($method:item)*
635        }
636
637        build: $build_closure:expr,
638    ) => {
639        impl_adapter_builder! {
640            builder_name: $Builder,
641            adapter_name: $Adapter,
642            config_type: $Config,
643            adapter_type: $adapter_type_str,
644            adapter_desc: $adapter_desc,
645            task_type: $TaskType,
646
647            fields: {},
648
649            methods: {
650                $($method)*
651            }
652
653            build: $build_closure,
654        }
655    };
656
657    // Variant without custom methods
658    (
659        builder_name: $Builder:ident,
660        adapter_name: $Adapter:ident,
661        config_type: $Config:ty,
662        adapter_type: $adapter_type_str:literal,
663        adapter_desc: $adapter_desc:literal,
664        task_type: $TaskType:ident,
665
666        fields: {
667            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
668            $(,)?
669        }
670
671        build: $build_closure:expr,
672    ) => {
673        impl_adapter_builder! {
674            builder_name: $Builder,
675            adapter_name: $Adapter,
676            config_type: $Config,
677            adapter_type: $adapter_type_str,
678            adapter_desc: $adapter_desc,
679            task_type: $TaskType,
680
681            fields: {
682                $($field_vis $field_name : $field_ty = $field_default),*
683            },
684
685            methods: {}
686
687            build: $build_closure,
688        }
689    };
690
691    // Minimal variant (no custom fields, no custom methods)
692    (
693        builder_name: $Builder:ident,
694        adapter_name: $Adapter:ident,
695        config_type: $Config:ty,
696        adapter_type: $adapter_type_str:literal,
697        adapter_desc: $adapter_desc:literal,
698        task_type: $TaskType:ident
699
700        build: $build_closure:expr,
701    ) => {
702        impl_adapter_builder! {
703            builder_name: $Builder,
704            adapter_name: $Adapter,
705            config_type: $Config,
706            adapter_type: $adapter_type_str,
707            adapter_desc: $adapter_desc,
708            task_type: $TaskType,
709
710            fields: {},
711
712            methods: {}
713
714            build: $build_closure,
715        }
716    };
717
718    // Variant with trait method overrides (for with_config, adapter_type)
719    (
720        builder_name: $Builder:ident,
721        adapter_name: $Adapter:ident,
722        config_type: $Config:ty,
723        adapter_type: $adapter_type_str:literal,
724        adapter_desc: $adapter_desc:literal,
725        task_type: $TaskType:ident,
726
727        fields: {
728            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
729            $(,)?
730        },
731
732        methods: {
733            $($method:item)*
734        }
735
736        overrides: {
737            with_config: $with_config_closure:expr,
738            adapter_type: $adapter_type_closure:expr,
739        }
740
741        build: $build_closure:expr,
742    ) => {
743        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
744        $crate::__impl_adapter_builder_common! {
745            builder_name: $Builder,
746            adapter_name: $Adapter,
747            config_type: $Config,
748            adapter_type: $adapter_type_str,
749            adapter_desc: $adapter_desc,
750            task_type: $TaskType,
751
752            fields: {
753                $($field_vis $field_name : $field_ty = $field_default),*
754            },
755
756            methods: {
757                $($method)*
758            }
759        }
760
761        // Generate AdapterBuilder impl with overridden methods
762        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
763            type Config = $Config;
764            type Adapter = $Adapter;
765
766            fn build(self, model_path: &std::path::Path) -> Result<Self::Adapter, $crate::core::OCRError> {
767                let build_fn: fn(Self, &std::path::Path) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
768                build_fn(self, model_path)
769            }
770
771            fn with_config(self, config: Self::Config) -> Self {
772                let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
773                with_config_fn(self, config)
774            }
775
776            fn adapter_type(&self) -> &str {
777                let adapter_type_fn: fn(&Self) -> &str = $adapter_type_closure;
778                adapter_type_fn(self)
779            }
780        }
781    };
782
783    // Variant with only with_config override
784    (
785        builder_name: $Builder:ident,
786        adapter_name: $Adapter:ident,
787        config_type: $Config:ty,
788        adapter_type: $adapter_type_str:literal,
789        adapter_desc: $adapter_desc:literal,
790        task_type: $TaskType:ident,
791
792        fields: {
793            $($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
794            $(,)?
795        },
796
797        methods: {
798            $($method:item)*
799        }
800
801        overrides: {
802            with_config: $with_config_closure:expr,
803        }
804
805        build: $build_closure:expr,
806    ) => {
807        // Generate common parts (struct, new, base_adapter_info, Default, OrtConfigurable)
808        $crate::__impl_adapter_builder_common! {
809            builder_name: $Builder,
810            adapter_name: $Adapter,
811            config_type: $Config,
812            adapter_type: $adapter_type_str,
813            adapter_desc: $adapter_desc,
814            task_type: $TaskType,
815
816            fields: {
817                $($field_vis $field_name : $field_ty = $field_default),*
818            },
819
820            methods: {
821                $($method)*
822            }
823        }
824
825        // Generate AdapterBuilder impl with with_config override
826        impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
827            type Config = $Config;
828            type Adapter = $Adapter;
829
830            fn build(self, model_path: &std::path::Path) -> Result<Self::Adapter, $crate::core::OCRError> {
831                let build_fn: fn(Self, &std::path::Path) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
832                build_fn(self, model_path)
833            }
834
835            fn with_config(self, config: Self::Config) -> Self {
836                let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
837                with_config_fn(self, config)
838            }
839
840            fn adapter_type(&self) -> &str {
841                $adapter_type_str
842            }
843        }
844    };
845}
846
847/// Macro to conditionally apply OrtSessionConfig to any builder that has `with_ort_config`.
848///
849/// This macro eliminates the repeated pattern:
850/// ```rust,no_run
851/// // let mut builder = SomeBuilder::new();
852/// // if let Some(ort_config) = ort_config {
853/// //     builder = builder.with_ort_config(ort_config);
854/// // }
855/// ```
856///
857/// Instead, use:
858/// ```rust,no_run
859/// // let builder = apply_ort_config!(SomeBuilder::new(), ort_config);
860/// ```
861///
862/// # Usage
863///
864/// ```rust,no_run
865/// // Works with any builder that has a `with_ort_config` method:
866/// // let builder = apply_ort_config!(
867/// //     DBModelBuilder::new()
868/// //         .preprocess_config(config),
869/// //     ort_config
870/// // );
871/// ```
872#[macro_export]
873macro_rules! apply_ort_config {
874    ($builder:expr, $ort_config:expr) => {{
875        let builder = $builder;
876        if let Some(cfg) = $ort_config {
877            builder.with_ort_config(cfg)
878        } else {
879            builder
880        }
881    }};
882}
883
884#[cfg(test)]
885mod tests {
886
887    // Test configuration structs
888    #[derive(Debug, Default)]
889    struct TestConfig {
890        simple_field: Option<String>,
891        nested_config: Option<NestedConfig>,
892        enable_field: Option<EnabledFeature>,
893    }
894
895    #[derive(Debug, Default)]
896    struct NestedConfig {
897        nested_field: Option<i32>,
898    }
899
900    impl NestedConfig {
901        fn new() -> Self {
902            Self::default()
903        }
904    }
905
906    #[derive(Debug, Default)]
907    struct EnabledFeature {
908        _enabled: bool,
909    }
910
911    // Test builder struct
912    #[derive(Debug)]
913    struct TestBuilder {
914        config: TestConfig,
915    }
916
917    impl TestBuilder {
918        fn new() -> Self {
919            Self {
920                config: TestConfig::default(),
921            }
922        }
923
924        fn get_config(&self) -> &TestConfig {
925            &self.config
926        }
927    }
928
929    // Apply the macro to generate builder methods (separate calls for each type)
930    impl_complete_builder! {
931        builder: TestBuilder,
932        config_field: config,
933        simple_setters: {
934            simple_field: String => "Sets a simple field value",
935        }
936    }
937
938    impl_complete_builder! {
939        builder: TestBuilder,
940        config_field: config,
941        nested_setters: {
942            nested_config: NestedConfig => {
943                nested_field: i32 => "Sets a nested field value",
944            },
945        }
946    }
947
948    impl_complete_builder! {
949        builder: TestBuilder,
950        config_field: config,
951        enable_methods: {
952            enable_feature => enable_field: EnabledFeature => "Enables a feature with default configuration",
953        }
954    }
955
956    #[test]
957    fn test_impl_complete_builder_nested_setter() {
958        let builder = TestBuilder::new().nested_field(42);
959
960        assert!(builder.get_config().nested_config.is_some());
961        let Some(nested) = builder.get_config().nested_config.as_ref() else {
962            panic!("expected nested_config to be Some");
963        };
964        assert_eq!(nested.nested_field, Some(42));
965    }
966
967    #[test]
968    fn test_impl_complete_builder_enable_method() {
969        let builder = TestBuilder::new().enable_feature();
970
971        assert!(builder.get_config().enable_field.is_some());
972    }
973
974    #[test]
975    fn test_impl_complete_builder_chaining() {
976        let builder = TestBuilder::new()
977            .simple_field("test".to_string())
978            .nested_field(123)
979            .enable_feature();
980
981        let config = builder.get_config();
982        assert_eq!(config.simple_field, Some("test".to_string()));
983        assert!(config.nested_config.is_some());
984        let Some(nested) = config.nested_config.as_ref() else {
985            panic!("expected nested_config to be Some");
986        };
987        assert_eq!(nested.nested_field, Some(123));
988        assert!(config.enable_field.is_some());
989    }
990}