#[cfg(feature = "quality")]
pub mod quality;
#[cfg(feature = "quality")]
pub mod string_utils;
#[cfg(any(
feature = "ocr",
feature = "ocr-wasm",
feature = "auto-rotate",
feature = "layout-detection",
feature = "pdf",
))]
pub mod image_decode;
pub mod json_utils;
pub mod markdown_utils;
pub mod pool;
pub mod pool_sizing;
pub mod string_pool;
pub mod timing;
pub mod xml_utils;
#[cfg(feature = "quality")]
pub use quality::{calculate_quality_score, clean_extracted_text, normalize_spaces};
#[cfg(feature = "quality")]
pub use string_utils::{calculate_text_confidence, fix_mojibake, safe_decode};
pub use pool::{
ByteBufferPool, Pool, PoolError, PoolGuard, Recyclable, StringBufferPool, create_byte_buffer_pool,
create_string_buffer_pool,
};
pub use pool_sizing::{PoolSizeHint, estimate_pool_size};
pub use json_utils::{camel_to_snake, snake_to_camel};
pub use xml_utils::xml_tag_name;
use std::borrow::Cow;
#[inline]
pub fn escape_html_entities(text: &str) -> Cow<'_, str> {
let needs_amp = text.contains('&');
let needs_lt = text.contains('<');
let needs_gt = text.contains('>');
if !needs_amp && !needs_lt && !needs_gt {
return Cow::Borrowed(text);
}
let mut result = String::with_capacity(text.len() + 16);
for ch in text.chars() {
match ch {
'&' => result.push_str("&"),
'<' => result.push_str("<"),
'>' => result.push_str(">"),
_ => result.push(ch),
}
}
Cow::Owned(result)
}
#[inline]
pub fn normalize_whitespace(s: &str) -> Cow<'_, str> {
let needs_normalization = s
.as_bytes()
.windows(2)
.any(|w| w[0].is_ascii_whitespace() && w[1].is_ascii_whitespace())
|| s.bytes().any(|b| b != b' ' && b.is_ascii_whitespace());
if needs_normalization {
Cow::Owned(s.split_whitespace().collect::<Vec<_>>().join(" "))
} else {
Cow::Borrowed(s)
}
}