use std::pin::Pin;
#[cfg(not(target_arch = "wasm32"))]
pub mod http_simple;
#[cfg(all(feature = "tesseract", not(target_arch = "wasm32")))]
pub mod tesseract;
#[derive(Debug, Clone)]
pub struct OcrResult {
pub text: String,
pub bbox: [f32; 4],
pub confidence: f32,
}
pub struct OcrOptions {
pub language: String,
}
#[cfg(not(target_arch = "wasm32"))]
pub trait OcrEngine: Send + Sync {
fn name(&self) -> &str;
fn recognize<'a, 'b: 'a, 'c: 'a>(
&'a self,
image_data: &'c [u8],
width: u32,
height: u32,
options: &'b OcrOptions,
) -> Pin<
Box<
dyn Future<Output = Result<Vec<OcrResult>, Box<dyn std::error::Error + Send + Sync>>>
+ Send
+ '_,
>,
>;
}
#[cfg(target_arch = "wasm32")]
pub trait OcrEngine: Send + Sync {
fn name(&self) -> &str;
fn recognize<'a, 'b: 'a, 'c: 'a>(
&'a self,
image_data: &'c [u8],
width: u32,
height: u32,
options: &'b OcrOptions,
) -> Pin<
Box<
dyn Future<Output = Result<Vec<OcrResult>, Box<dyn std::error::Error + Send + Sync>>>
+ '_,
>,
>;
}
#[cfg(test)]
mod tests {
use super::*;
struct DummyEngine;
impl OcrEngine for DummyEngine {
fn name(&self) -> &str {
"dummy"
}
fn recognize<'a, 'b: 'a, 'c: 'a>(
&'a self,
_image_data: &'c [u8],
_width: u32,
_height: u32,
options: &'b OcrOptions,
) -> Pin<
Box<
dyn Future<
Output = Result<Vec<OcrResult>, Box<dyn std::error::Error + Send + Sync>>,
> + Send
+ '_,
>,
> {
Box::pin(async move {
Ok(vec![OcrResult {
text: format!("lang={}", options.language),
bbox: [0.0, 0.0, 10.0, 10.0],
confidence: 0.9,
}])
})
}
}
#[tokio::test]
async fn test_engine_trait_object() {
let engine: Box<dyn OcrEngine> = Box::new(DummyEngine);
assert_eq!(engine.name(), "dummy");
let opts = OcrOptions {
language: "eng".into(),
};
let r = engine.recognize(&[], 1, 1, &opts).await.unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].text, "lang=eng");
assert_eq!(r[0].bbox, [0.0, 0.0, 10.0, 10.0]);
assert!((r[0].confidence - 0.9).abs() < 1e-6);
}
}