use std::path::Path;
use anyhow::{Context, Result};
pub const MULTIMODAL_EXTENSIONS: &[&str] = &["pdf"];
pub fn is_multimodal_file(path: &Path) -> bool {
match path.extension().and_then(|e| e.to_str()) {
Some(ext) => {
let ext = ext.to_ascii_lowercase();
MULTIMODAL_EXTENSIONS.contains(&ext.as_str())
}
None => false,
}
}
pub fn extract_text(path: &Path) -> Result<String> {
let ext = path
.extension()
.and_then(|e| e.to_str())
.map(|e| e.to_ascii_lowercase())
.unwrap_or_default();
match ext.as_str() {
"pdf" => extract_pdf_text(path),
other => anyhow::bail!("multimodal extractor: unsupported extension {other:?}"),
}
}
fn extract_pdf_text(path: &Path) -> Result<String> {
pdf_extract::extract_text(path)
.with_context(|| format!("failed to extract text from PDF {}", path.display()))
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn detects_pdf_extension_case_insensitively() {
assert!(is_multimodal_file(&PathBuf::from("docs/runbook.pdf")));
assert!(is_multimodal_file(&PathBuf::from("docs/RUNBOOK.PDF")));
assert!(!is_multimodal_file(&PathBuf::from("src/main.rs")));
assert!(!is_multimodal_file(&PathBuf::from("README.md")));
assert!(!is_multimodal_file(&PathBuf::from("no_extension")));
}
#[test]
fn extract_text_rejects_unsupported_extension() {
let err = extract_text(&PathBuf::from("photo.png")).unwrap_err();
assert!(err.to_string().contains("unsupported extension"));
}
#[test]
fn extracts_text_from_sample_pdf() {
let fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/sample.pdf");
let text = extract_text(&fixture).expect("extract sample PDF");
let normalized: String = text.split_whitespace().collect::<Vec<_>>().join(" ");
assert!(
normalized.contains("Hello Bobbin multimodal PDF ingest"),
"extracted text was: {text:?}",
);
}
}