use crate::grep::error::{GrepError, GrepResult};
pub fn extract_text(data: &[u8]) -> GrepResult<String> {
use docx_rs::read_docx;
let docx = read_docx(data).map_err(|e| GrepError::DocumentExtraction {
file_path: std::path::PathBuf::from("<memory>"),
message: format!("DOCX parsing failed: {:?}", e),
})?;
let mut text_parts = Vec::new();
for child in &docx.document.children {
extract_text_from_content(child, &mut text_parts);
}
Ok(text_parts.join("\n"))
}
fn extract_text_from_content(content: &docx_rs::DocumentChild, parts: &mut Vec<String>) {
match content {
docx_rs::DocumentChild::Paragraph(para) => {
let mut para_text = String::new();
for child in ¶.children {
extract_text_from_paragraph_child(child, &mut para_text);
}
if !para_text.is_empty() {
parts.push(para_text);
}
}
docx_rs::DocumentChild::Table(table) => {
for row in &table.rows {
extract_text_from_table_row(row, parts);
}
}
_ => {}
}
}
fn extract_text_from_table_row(row: &docx_rs::TableChild, parts: &mut Vec<String>) {
#[allow(irrefutable_let_patterns)]
if let docx_rs::TableChild::TableRow(tr) = row {
for cell in &tr.cells {
extract_text_from_table_cell(cell, parts);
}
}
}
fn extract_text_from_table_cell(cell: &docx_rs::TableRowChild, parts: &mut Vec<String>) {
#[allow(irrefutable_let_patterns)]
if let docx_rs::TableRowChild::TableCell(tc) = cell {
for child in &tc.children {
if let docx_rs::TableCellContent::Paragraph(para) = child {
let mut para_text = String::new();
for pchild in ¶.children {
extract_text_from_paragraph_child(pchild, &mut para_text);
}
if !para_text.is_empty() {
parts.push(para_text);
}
}
}
}
}
fn extract_text_from_paragraph_child(child: &docx_rs::ParagraphChild, text: &mut String) {
match child {
docx_rs::ParagraphChild::Run(run) => {
for run_child in &run.children {
if let docx_rs::RunChild::Text(t) = run_child {
text.push_str(&t.text);
}
}
}
docx_rs::ParagraphChild::Hyperlink(link) => {
for run in &link.children {
if let docx_rs::ParagraphChild::Run(r) = run {
for run_child in &r.children {
if let docx_rs::RunChild::Text(t) = run_child {
text.push_str(&t.text);
}
}
}
}
}
_ => {}
}
}
pub fn extract_text_from_file(path: &std::path::Path) -> GrepResult<String> {
let data = std::fs::read(path).map_err(GrepError::Io)?;
extract_text(&data)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_invalid_docx() {
let invalid_data = b"This is not a DOCX file";
let result = extract_text(invalid_data);
assert!(result.is_err());
}
#[test]
fn test_extract_empty_zip() {
let empty_zip = [
0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let result = extract_text(&empty_zip);
assert!(result.is_err());
}
}