easydoc_reader/extractor/
text.rs1use std::path::Path;
4
5use easydoc_core::{DocError, Result};
6
7use crate::extractor::{DocumentFormat, detect_format_from_bytes};
8
9pub fn extract_text(path: &Path) -> Result<String> {
17 let doc = office_oxide::Document::open(path)
18 .map_err(|e| DocError::Document(format!("failed to open document: {e}")))?;
19 Ok(doc.plain_text())
20}
21
22pub fn extract_text_from_bytes(bytes: &[u8]) -> Result<String> {
32 let format = detect_format_from_bytes(bytes).ok_or_else(|| {
33 DocError::Format("unsupported document: could not detect DOCX/DOC magic bytes".to_owned())
34 })?;
35 let doc =
36 office_oxide::Document::from_reader(std::io::Cursor::new(bytes.to_vec()), to_oxide(format))
37 .map_err(|e| DocError::Document(format!("failed to open document from bytes: {e}")))?;
38 Ok(doc.plain_text())
39}
40
41fn to_oxide(format: DocumentFormat) -> office_oxide::DocumentFormat {
43 match format {
44 DocumentFormat::Docx => office_oxide::DocumentFormat::Docx,
45 DocumentFormat::Doc => office_oxide::DocumentFormat::Doc,
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use easydoc_writer::DocBuilder;
53
54 fn minimal_docx_bytes() -> Vec<u8> {
55 DocBuilder::new("memory.docx")
56 .add_paragraph(easydoc_writer::Paragraph::new().add_text("hello bytes"))
57 .save_to_bytes()
58 .expect("writer should produce valid DOCX")
59 }
60
61 #[test]
62 fn detect_format_from_bytes_docx() {
63 let bytes = minimal_docx_bytes();
64 assert_eq!(detect_format_from_bytes(&bytes), Some(DocumentFormat::Docx));
65 }
66
67 #[test]
68 fn detect_format_from_bytes_doc_magic() {
69 let ole2: [u8; 8] = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];
70 assert_eq!(detect_format_from_bytes(&ole2), Some(DocumentFormat::Doc));
71 }
72
73 #[test]
74 fn detect_format_from_bytes_garbage() {
75 assert_eq!(detect_format_from_bytes(b"not a document"), None);
76 assert_eq!(detect_format_from_bytes(b""), None);
77 assert_eq!(detect_format_from_bytes(&[0xd0, 0xcf]), None); }
79
80 #[test]
81 fn extract_text_from_bytes_roundtrip() {
82 let bytes = minimal_docx_bytes();
83 let text = extract_text_from_bytes(&bytes).expect("should parse");
84 assert!(text.contains("hello bytes"));
85 }
86
87 #[test]
88 fn extract_text_from_bytes_garbage_errors() {
89 assert!(extract_text_from_bytes(b"garbage").is_err());
90 }
91}