use mktemp::Temp;
use std::collections::HashSet;
use std::fs::{create_dir_all, File};
use std::io::{BufWriter, Write};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use zip::write::FileOptions;
#[allow(dead_code)]
pub(crate) struct TempZip {
zipped: PathBuf,
temp_path: Temp,
entries: Vec<TempZipEntry>,
}
pub(crate) struct TempWrite<'a> {
temp_file: BufWriter<File>,
_dummy: PhantomData<&'a TempZip>,
}
#[allow(dead_code)]
struct TempZipEntry {
name: String,
is_dir: bool,
fopt: FileOptions,
}
#[allow(dead_code)]
impl TempZip {
pub(crate) fn new(zip_file: &Path) -> Result<Self, std::io::Error> {
Ok(TempZip {
zipped: zip_file.to_path_buf(),
temp_path: mktemp::Temp::new_dir_in(zip_file.parent().unwrap())?,
entries: Vec::new(),
})
}
pub(crate) fn add_directory<S: Into<String>>(
&mut self,
name: S,
options: FileOptions,
) -> Result<(), std::io::Error> {
let name = name.into();
let add = self.temp_path.join(&name);
create_dir_all(add)?;
self.entries.push(TempZipEntry {
is_dir: true,
name,
fopt: options,
});
Ok(())
}
pub(crate) fn start_file<'a>(
&'a mut self,
name: &'_ str,
fopt: FileOptions,
) -> Result<TempWrite<'_>, std::io::Error> {
let file = self.temp_path.join(name);
let path = file.parent().unwrap();
create_dir_all(path)?;
self.entries.push(TempZipEntry {
is_dir: false,
name: name.to_string(),
fopt,
});
Ok(TempWrite {
temp_file: BufWriter::new(File::create(file)?),
_dummy: Default::default(),
})
}
pub(crate) fn zip(self) -> Result<(), std::io::Error> {
let zip_file = File::create(&self.zipped)?;
let mut zip_writer = zip::ZipWriter::new(BufWriter::new(zip_file));
let mut names = HashSet::new();
for entry in self.entries.into_iter() {
if names.contains(&entry.name) {
} else if !entry.is_dir {
zip_writer.start_file(&entry.name, entry.fopt)?;
let file = self.temp_path.join(&entry.name);
let buf = std::fs::read(file)?;
zip_writer.write_all(buf.as_slice())?;
} else {
zip_writer.add_directory(&entry.name, entry.fopt)?;
}
names.insert(entry.name);
}
Ok(())
}
}
impl Write for TempWrite<'_> {
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
self.temp_file.write(buf)
}
fn flush(&mut self) -> Result<(), std::io::Error> {
self.temp_file.flush()
}
}