use std::io::{Cursor, Read};
pub struct DocCfb<'a> {
compound: cfb::CompoundFile<Cursor<&'a [u8]>>,
}
impl<'a> DocCfb<'a> {
pub fn open(bytes: &'a [u8]) -> Result<Self, String> {
let compound = cfb::CompoundFile::open(Cursor::new(bytes))
.map_err(|e| format!("Cannot open .doc file (invalid CFB format): {e}"))?;
Ok(DocCfb { compound })
}
pub fn word_document_stream(&mut self) -> Result<Vec<u8>, String> {
let mut buf = Vec::new();
self.compound
.open_stream("/WordDocument")
.map_err(|_| "Missing WordDocument stream — not a valid .doc file".to_string())?
.read_to_end(&mut buf)
.map_err(|e| format!("Failed to read WordDocument stream: {e}"))?;
Ok(buf)
}
pub fn table_stream(&mut self, which: u8) -> Result<Vec<u8>, String> {
let stream_name = if which == 1 { "/1Table" } else { "/0Table" };
let mut buf = Vec::new();
self.compound
.open_stream(stream_name)
.map_err(|_| format!("Missing {stream_name} stream — not a valid .doc file"))?
.read_to_end(&mut buf)
.map_err(|e| format!("Failed to read {stream_name}: {e}"))?;
Ok(buf)
}
pub fn data_stream(&mut self) -> Result<Option<Vec<u8>>, String> {
let mut stream = match self.compound.open_stream("/Data") {
Ok(s) => s,
Err(_) => return Ok(None),
};
let mut buf = Vec::new();
stream
.read_to_end(&mut buf)
.map_err(|e| format!("Failed to read Data stream: {e}"))?;
Ok(Some(buf))
}
}