#[macro_export]
macro_rules! with_task_registry {
($callback:path) => {
$callback! {
TextDetection {
output: $crate::domain::tasks::TextDetectionOutput,
adapter: $crate::domain::adapters::TextDetectionAdapter,
constructor: text_detection,
conversion: into_text_detection,
doc: "Text detection - locating text regions in images",
},
TextRecognition {
output: $crate::domain::tasks::TextRecognitionOutput,
adapter: $crate::domain::adapters::TextRecognitionAdapter,
constructor: text_recognition,
conversion: into_text_recognition,
doc: "Text recognition - converting text regions to strings",
},
DocumentOrientation {
output: $crate::domain::tasks::DocumentOrientationOutput,
adapter: $crate::domain::adapters::DocumentOrientationAdapter,
constructor: document_orientation,
conversion: into_document_orientation,
doc: "Document orientation classification",
},
TextLineOrientation {
output: $crate::domain::tasks::TextLineOrientationOutput,
adapter: $crate::domain::adapters::TextLineOrientationAdapter,
constructor: text_line_orientation,
conversion: into_text_line_orientation,
doc: "Text line orientation classification",
},
DocumentRectification {
output: $crate::domain::tasks::DocumentRectificationOutput,
adapter: $crate::domain::adapters::UVDocRectifierAdapter,
constructor: document_rectification,
conversion: into_document_rectification,
doc: "Document rectification/unwarp",
},
LayoutDetection {
output: $crate::domain::tasks::LayoutDetectionOutput,
adapter: $crate::domain::adapters::LayoutDetectionAdapter,
constructor: layout_detection,
conversion: into_layout_detection,
doc: "Layout detection/analysis",
},
TableCellDetection {
output: $crate::domain::tasks::TableCellDetectionOutput,
adapter: $crate::domain::adapters::TableCellDetectionAdapter,
constructor: table_cell_detection,
conversion: into_table_cell_detection,
doc: "Table cell detection - locating cells within table regions",
},
FormulaRecognition {
output: $crate::domain::tasks::FormulaRecognitionOutput,
adapter: $crate::domain::adapters::FormulaRecognitionAdapter,
constructor: formula_recognition,
conversion: into_formula_recognition,
doc: "Formula recognition - converting mathematical formulas to LaTeX",
},
SealTextDetection {
output: $crate::domain::tasks::SealTextDetectionOutput,
adapter: $crate::domain::adapters::SealTextDetectionAdapter,
constructor: seal_text_detection,
conversion: into_seal_text_detection,
doc: "Seal text detection - locating text regions in seal/stamp images",
},
TableClassification {
output: $crate::domain::tasks::TableClassificationOutput,
adapter: $crate::domain::adapters::TableClassificationAdapter,
constructor: table_classification,
conversion: into_table_classification,
doc: "Table classification - classifying table images as wired or wireless",
},
TableStructureRecognition {
output: $crate::domain::tasks::TableStructureRecognitionOutput,
adapter: $crate::domain::adapters::TableStructureRecognitionAdapter,
constructor: table_structure_recognition,
conversion: into_table_structure_recognition,
doc: "Table structure recognition - recognizing table structure as HTML with bboxes",
}
}
};
}
#[macro_export]
macro_rules! impl_task_type_enum {
($(
$task:ident {
output: $output:ty,
adapter: $adapter:ty,
constructor: $constructor:ident,
conversion: $conversion:ident,
doc: $doc:literal,
}
),* $(,)?) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum TaskType {
$(
#[doc = $doc]
$task,
)*
}
impl TaskType {
pub fn name(&self) -> &'static str {
match self {
$(TaskType::$task => <$output as $crate::core::traits::TaskDefinition>::TASK_NAME,)*
}
}
}
};
}
#[macro_export]
macro_rules! with_nested {
($field:expr, $type:ty, $var:ident => $body:block) => {
if $field.is_none() {
$field = Some(<$type>::new());
}
if let Some(ref mut $var) = $field {
$body
}
};
}
#[macro_export]
macro_rules! metrics {
($success:expr, $failure:expr, $start_time:expr; $($key:ident = $value:expr),*) => {
{
let mut metrics = $crate::pipeline::stages::StageMetrics::new($success, $failure);
metrics = metrics.with_processing_time($start_time.elapsed());
$(
metrics = metrics.with_info(stringify!($key), $value.to_string());
)*
metrics
}
};
($success:expr, $failure:expr; $($key:ident = $value:expr),*) => {
{
let mut metrics = $crate::pipeline::stages::StageMetrics::new($success, $failure);
$(
metrics = metrics.with_info(stringify!($key), $value.to_string());
)*
metrics
}
};
}
#[macro_export]
macro_rules! impl_complete_builder {
(
builder: $builder:ident,
config_field: $config_field:ident,
simple_setters: {
$($simple_field:ident: $simple_type:ty => $simple_doc:literal),* $(,)?
}
) => {
impl $builder {
$(
#[doc = $simple_doc]
pub fn $simple_field(mut self, value: $simple_type) -> Self {
self.$config_field.$simple_field = Some(value);
self
}
)*
}
};
(
builder: $builder:ident,
config_field: $config_field:ident,
nested_setters: {
$($nested_path:ident: $nested_type:ty => {
$($nested_field:ident: $nested_field_type:ty => $nested_doc:literal),* $(,)?
}),* $(,)?
}
) => {
impl $builder {
$($(
#[doc = $nested_doc]
pub fn $nested_field(mut self, value: $nested_field_type) -> Self {
$crate::with_nested!(self.$config_field.$nested_path, $nested_type, config => {
config.$nested_field = Some(value);
});
self
}
)*)*
}
};
(
builder: $builder:ident,
config_field: $config_field:ident,
enable_methods: {
$($enable_method:ident => $enable_field:ident: $enable_type:ty => $enable_doc:literal),* $(,)?
}
) => {
impl $builder {
$(
#[doc = $enable_doc]
pub fn $enable_method(mut self) -> Self {
self.$config_field.$enable_field = Some(<$enable_type>::default());
self
}
)*
}
};
}
#[macro_export]
macro_rules! impl_config_new_and_with_common {
(
$Config:ident,
common_defaults: ($model_name_opt:expr, $batch_size_opt:expr),
fields: { $( $field:ident : $default_expr:expr ),* $(,)? }
) => {
impl $Config {
pub fn new() -> Self {
Self {
common: $crate::core::config::builder::ModelInferenceConfig::with_defaults(
$model_name_opt, $batch_size_opt
),
$( $field: $default_expr ),*
}
}
pub fn with_common(common: $crate::core::config::builder::ModelInferenceConfig) -> Self {
Self {
common,
$( $field: $default_expr ),*
}
}
}
};
}
#[macro_export]
macro_rules! impl_common_builder_methods {
($Builder:ident, $common_field:ident) => {
impl $Builder {
pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
self.$common_field = self.$common_field.model_path(model_path);
self
}
pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
self.$common_field = self.$common_field.model_name(model_name);
self
}
pub fn batch_size(mut self, batch_size: usize) -> Self {
self.$common_field = self.$common_field.batch_size(batch_size);
self
}
pub fn enable_logging(mut self, enable: bool) -> Self {
self.$common_field = self.$common_field.enable_logging(enable);
self
}
pub fn ort_session(
mut self,
config: $crate::core::config::onnx::OrtSessionConfig,
) -> Self {
self.$common_field = self.$common_field.ort_session(config);
self
}
}
};
}
#[macro_export]
macro_rules! common_builder_methods {
($common_field:ident) => {
pub fn model_path(mut self, model_path: impl Into<std::path::PathBuf>) -> Self {
self.$common_field = self.$common_field.model_path(model_path);
self
}
pub fn model_name(mut self, model_name: impl Into<String>) -> Self {
self.$common_field = self.$common_field.model_name(model_name);
self
}
pub fn batch_size(mut self, batch_size: usize) -> Self {
self.$common_field = self.$common_field.batch_size(batch_size);
self
}
pub fn enable_logging(mut self, enable: bool) -> Self {
self.$common_field = self.$common_field.enable_logging(enable);
self
}
pub fn ort_session(mut self, config: $crate::core::config::onnx::OrtSessionConfig) -> Self {
self.$common_field = self.$common_field.ort_session(config);
self
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_adapter_builder_common {
(
builder_name: $Builder:ident,
adapter_name: $Adapter:ident,
config_type: $Config:ty,
adapter_type: $adapter_type_str:literal,
adapter_desc: $adapter_desc:literal,
task_type: $TaskType:ident,
fields: {
$($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
$(,)?
},
methods: {
$($method:item)*
}
) => {
#[doc = $adapter_desc]
#[derive(Debug)]
pub struct $Builder {
/// Common configuration shared across all adapters
config: $crate::domain::adapters::builder_config::AdapterBuilderConfig<$Config>,
$($field_vis $field_name : $field_ty),*
}
impl $Builder {
pub fn new() -> Self {
Self {
config: $crate::domain::adapters::builder_config::AdapterBuilderConfig::default(),
$($field_name : $field_default),*
}
}
pub fn base_adapter_info() -> $crate::core::traits::adapter::AdapterInfo {
$crate::core::traits::adapter::AdapterInfo::new(
$adapter_type_str,
$crate::core::traits::task::TaskType::$TaskType,
$adapter_desc,
)
}
$($method)*
}
impl Default for $Builder {
fn default() -> Self {
Self::new()
}
}
impl $crate::core::traits::OrtConfigurable for $Builder {
fn with_ort_config(mut self, config: $crate::core::config::OrtSessionConfig) -> Self {
self.config = self.config.with_ort_config(config);
self
}
}
};
}
#[macro_export]
macro_rules! impl_adapter_builder {
(
builder_name: $Builder:ident,
adapter_name: $Adapter:ident,
config_type: $Config:ty,
adapter_type: $adapter_type_str:literal,
adapter_desc: $adapter_desc:literal,
task_type: $TaskType:ident,
fields: {
$($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
$(,)?
},
methods: {
$($method:item)*
}
build: $build_closure:expr,
) => {
$crate::__impl_adapter_builder_common! {
builder_name: $Builder,
adapter_name: $Adapter,
config_type: $Config,
adapter_type: $adapter_type_str,
adapter_desc: $adapter_desc,
task_type: $TaskType,
fields: {
$($field_vis $field_name : $field_ty = $field_default),*
},
methods: {
pub fn with_config(mut self, config: $Config) -> Self {
self.config = self.config.with_task_config(config);
self
}
$($method)*
}
}
impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
type Config = $Config;
type Adapter = $Adapter;
fn build(self, model_path: &std::path::Path) -> Result<Self::Adapter, $crate::core::OCRError> {
let build_fn: fn(Self, &std::path::Path) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
build_fn(self, model_path)
}
fn with_config(mut self, config: Self::Config) -> Self {
self.config = self.config.with_task_config(config);
self
}
fn adapter_type(&self) -> &str {
$adapter_type_str
}
}
};
(
builder_name: $Builder:ident,
adapter_name: $Adapter:ident,
config_type: $Config:ty,
adapter_type: $adapter_type_str:literal,
adapter_desc: $adapter_desc:literal,
task_type: $TaskType:ident,
methods: {
$($method:item)*
}
build: $build_closure:expr,
) => {
impl_adapter_builder! {
builder_name: $Builder,
adapter_name: $Adapter,
config_type: $Config,
adapter_type: $adapter_type_str,
adapter_desc: $adapter_desc,
task_type: $TaskType,
fields: {},
methods: {
$($method)*
}
build: $build_closure,
}
};
(
builder_name: $Builder:ident,
adapter_name: $Adapter:ident,
config_type: $Config:ty,
adapter_type: $adapter_type_str:literal,
adapter_desc: $adapter_desc:literal,
task_type: $TaskType:ident,
fields: {
$($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
$(,)?
}
build: $build_closure:expr,
) => {
impl_adapter_builder! {
builder_name: $Builder,
adapter_name: $Adapter,
config_type: $Config,
adapter_type: $adapter_type_str,
adapter_desc: $adapter_desc,
task_type: $TaskType,
fields: {
$($field_vis $field_name : $field_ty = $field_default),*
},
methods: {}
build: $build_closure,
}
};
(
builder_name: $Builder:ident,
adapter_name: $Adapter:ident,
config_type: $Config:ty,
adapter_type: $adapter_type_str:literal,
adapter_desc: $adapter_desc:literal,
task_type: $TaskType:ident
build: $build_closure:expr,
) => {
impl_adapter_builder! {
builder_name: $Builder,
adapter_name: $Adapter,
config_type: $Config,
adapter_type: $adapter_type_str,
adapter_desc: $adapter_desc,
task_type: $TaskType,
fields: {},
methods: {}
build: $build_closure,
}
};
(
builder_name: $Builder:ident,
adapter_name: $Adapter:ident,
config_type: $Config:ty,
adapter_type: $adapter_type_str:literal,
adapter_desc: $adapter_desc:literal,
task_type: $TaskType:ident,
fields: {
$($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
$(,)?
},
methods: {
$($method:item)*
}
overrides: {
with_config: $with_config_closure:expr,
adapter_type: $adapter_type_closure:expr,
}
build: $build_closure:expr,
) => {
$crate::__impl_adapter_builder_common! {
builder_name: $Builder,
adapter_name: $Adapter,
config_type: $Config,
adapter_type: $adapter_type_str,
adapter_desc: $adapter_desc,
task_type: $TaskType,
fields: {
$($field_vis $field_name : $field_ty = $field_default),*
},
methods: {
$($method)*
}
}
impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
type Config = $Config;
type Adapter = $Adapter;
fn build(self, model_path: &std::path::Path) -> Result<Self::Adapter, $crate::core::OCRError> {
let build_fn: fn(Self, &std::path::Path) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
build_fn(self, model_path)
}
fn with_config(self, config: Self::Config) -> Self {
let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
with_config_fn(self, config)
}
fn adapter_type(&self) -> &str {
let adapter_type_fn: fn(&Self) -> &str = $adapter_type_closure;
adapter_type_fn(self)
}
}
};
(
builder_name: $Builder:ident,
adapter_name: $Adapter:ident,
config_type: $Config:ty,
adapter_type: $adapter_type_str:literal,
adapter_desc: $adapter_desc:literal,
task_type: $TaskType:ident,
fields: {
$($field_vis:vis $field_name:ident : $field_ty:ty = $field_default:expr),*
$(,)?
},
methods: {
$($method:item)*
}
overrides: {
with_config: $with_config_closure:expr,
}
build: $build_closure:expr,
) => {
$crate::__impl_adapter_builder_common! {
builder_name: $Builder,
adapter_name: $Adapter,
config_type: $Config,
adapter_type: $adapter_type_str,
adapter_desc: $adapter_desc,
task_type: $TaskType,
fields: {
$($field_vis $field_name : $field_ty = $field_default),*
},
methods: {
$($method)*
}
}
impl $crate::core::traits::adapter::AdapterBuilder for $Builder {
type Config = $Config;
type Adapter = $Adapter;
fn build(self, model_path: &std::path::Path) -> Result<Self::Adapter, $crate::core::OCRError> {
let build_fn: fn(Self, &std::path::Path) -> Result<$Adapter, $crate::core::OCRError> = $build_closure;
build_fn(self, model_path)
}
fn with_config(self, config: Self::Config) -> Self {
let with_config_fn: fn(Self, Self::Config) -> Self = $with_config_closure;
with_config_fn(self, config)
}
fn adapter_type(&self) -> &str {
$adapter_type_str
}
}
};
}
#[macro_export]
macro_rules! apply_ort_config {
($builder:expr, $ort_config:expr) => {{
let builder = $builder;
if let Some(cfg) = $ort_config {
builder.with_ort_config(cfg)
} else {
builder
}
}};
}
#[cfg(test)]
mod tests {
#[derive(Debug, Default)]
struct TestConfig {
simple_field: Option<String>,
nested_config: Option<NestedConfig>,
enable_field: Option<EnabledFeature>,
}
#[derive(Debug, Default)]
struct NestedConfig {
nested_field: Option<i32>,
}
impl NestedConfig {
fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Default)]
struct EnabledFeature {
_enabled: bool,
}
#[derive(Debug)]
struct TestBuilder {
config: TestConfig,
}
impl TestBuilder {
fn new() -> Self {
Self {
config: TestConfig::default(),
}
}
fn get_config(&self) -> &TestConfig {
&self.config
}
}
impl_complete_builder! {
builder: TestBuilder,
config_field: config,
simple_setters: {
simple_field: String => "Sets a simple field value",
}
}
impl_complete_builder! {
builder: TestBuilder,
config_field: config,
nested_setters: {
nested_config: NestedConfig => {
nested_field: i32 => "Sets a nested field value",
},
}
}
impl_complete_builder! {
builder: TestBuilder,
config_field: config,
enable_methods: {
enable_feature => enable_field: EnabledFeature => "Enables a feature with default configuration",
}
}
#[test]
fn test_impl_complete_builder_nested_setter() {
let builder = TestBuilder::new().nested_field(42);
assert!(builder.get_config().nested_config.is_some());
let Some(nested) = builder.get_config().nested_config.as_ref() else {
panic!("expected nested_config to be Some");
};
assert_eq!(nested.nested_field, Some(42));
}
#[test]
fn test_impl_complete_builder_enable_method() {
let builder = TestBuilder::new().enable_feature();
assert!(builder.get_config().enable_field.is_some());
}
#[test]
fn test_impl_complete_builder_chaining() {
let builder = TestBuilder::new()
.simple_field("test".to_string())
.nested_field(123)
.enable_feature();
let config = builder.get_config();
assert_eq!(config.simple_field, Some("test".to_string()));
assert!(config.nested_config.is_some());
let Some(nested) = config.nested_config.as_ref() else {
panic!("expected nested_config to be Some");
};
assert_eq!(nested.nested_field, Some(123));
assert!(config.enable_field.is_some());
}
}