use std::sync::Mutex; use anyhow::Result; use kebab_core::{Lang, OcrText}; use kebab_parse_image::OcrEngine; pub struct MockOcrEngine { expected_texts: Vec, call_index: Mutex, fail: bool, } impl MockOcrEngine { /// Single text (backward-compat ctor for pdf_ocr_apply.rs 10 sites). pub fn single(text: impl Into, fail: bool) -> Self { Self { expected_texts: vec![text.into()], call_index: Mutex::new(0), fail, } } /// Per-page texts (cursor advances per recognize call). pub fn per_page(texts: Vec, fail: bool) -> Self { Self { expected_texts: texts, call_index: Mutex::new(0), fail, } } } impl OcrEngine for MockOcrEngine { fn engine_name(&self) -> &'static str { "mock-ocr" } fn engine_version(&self) -> String { "mock-v1".to_string() } #[allow(clippy::unnecessary_literal_bound)] fn model(&self) -> &str { "mock-model" } fn recognize(&self, _img: &[u8], _hint: Option<&Lang>) -> Result { if self.fail { anyhow::bail!("mock failure"); } let mut idx = self.call_index.lock().unwrap(); let text = self .expected_texts .get(*idx) .cloned() .unwrap_or_else(|| self.expected_texts.last().cloned().unwrap_or_default()); *idx += 1; Ok(OcrText { joined: text, regions: vec![], engine: "mock-ocr".to_string(), engine_version: "mock-v1".to_string(), }) } }