use crate::ooxml::opc::error::{OpcError, Result};
use crate::ooxml::opc::packuri::PackURI;
use std::fs::File;
use std::io::{BufReader, Read, Seek};
use std::path::Path;
use zip::ZipArchive;
pub struct PhysPkgReader<R: Read + Seek> {
archive: ZipArchive<R>,
}
impl PhysPkgReader<BufReader<File>> {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
if !path.exists() {
return Err(OpcError::PackageNotFound(path.display().to_string()));
}
let file = File::open(path)?;
let buf_reader = BufReader::with_capacity(8192, file);
Self::new(buf_reader)
}
}
impl<R: Read + Seek> PhysPkgReader<R> {
pub fn new(reader: R) -> Result<Self> {
let archive = ZipArchive::new(reader)?;
Ok(Self { archive })
}
pub fn blob_for(&mut self, pack_uri: &PackURI) -> Result<Vec<u8>> {
let membername = pack_uri.membername();
let mut file = self
.archive
.by_name(membername)
.map_err(|_| OpcError::PartNotFound(pack_uri.to_string()))?;
let size = file.size() as usize;
let mut buffer = Vec::with_capacity(size);
file.read_to_end(&mut buffer)?;
Ok(buffer)
}
pub fn content_types_xml(&mut self) -> Result<Vec<u8>> {
let content_types_uri = PackURI::new(crate::ooxml::opc::packuri::CONTENT_TYPES_URI)
.map_err(OpcError::InvalidPackUri)?;
self.blob_for(&content_types_uri)
}
pub fn rels_xml_for(&mut self, source_uri: &PackURI) -> Result<Option<Vec<u8>>> {
let rels_uri = source_uri
.rels_uri()
.map_err(OpcError::InvalidPackUri)?;
match self.blob_for(&rels_uri) {
Ok(blob) => Ok(Some(blob)),
Err(OpcError::PartNotFound(_)) => Ok(None),
Err(e) => Err(e),
}
}
pub fn len(&self) -> usize {
self.archive.len()
}
pub fn is_empty(&self) -> bool {
self.archive.len() == 0
}
pub fn member_names(&mut self) -> Result<Vec<String>> {
let mut names = Vec::with_capacity(self.archive.len());
for i in 0..self.archive.len() {
let file = self.archive.by_index(i)?;
names.push(file.name().to_string());
}
Ok(names)
}
pub fn contains(&mut self, pack_uri: &PackURI) -> bool {
let membername = pack_uri.membername();
self.archive.by_name(membername).is_ok()
}
}
pub struct PhysPkgWriter {
archive: zip::ZipWriter<File>,
}
impl PhysPkgWriter {
pub fn create<P: AsRef<Path>>(path: P) -> Result<Self> {
let file = File::create(path)?;
let archive = zip::ZipWriter::new(file);
Ok(Self { archive })
}
pub fn write(&mut self, pack_uri: &PackURI, blob: &[u8]) -> Result<()> {
use zip::write::SimpleFileOptions;
let options = SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Deflated)
.compression_level(Some(6));
self.archive.start_file(pack_uri.membername(), options)?;
std::io::copy(&mut std::io::Cursor::new(blob), &mut self.archive)?;
Ok(())
}
pub fn finish(self) -> Result<()> {
self.archive.finish()?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_reader_from_cursor() {
let mut zip_data = Vec::new();
{
let cursor = Cursor::new(&mut zip_data);
let mut writer = zip::ZipWriter::new(cursor);
use zip::write::SimpleFileOptions;
let options = SimpleFileOptions::default();
writer.start_file("test.txt", options).unwrap();
std::io::Write::write_all(&mut writer, b"Hello, World!").unwrap();
writer.finish().unwrap();
}
let cursor = Cursor::new(zip_data);
let mut reader = PhysPkgReader::new(cursor).unwrap();
let pack_uri = PackURI::new("/test.txt").unwrap();
let content = reader.blob_for(&pack_uri).unwrap();
assert_eq!(content, b"Hello, World!");
}
}