use std::fs::File;
use std::path::{Path, PathBuf};
use cab::{CabinetBuilder, CabinetWriter};
use crate::archiver::{ArchiveEntry, ToteArchiver};
use crate::{Result, Error};
pub(super) struct Archiver {}
impl ToteArchiver for Archiver {
fn perform(
&self,
file: File,
targets: &[PathBuf],
config: &crate::ArchiveConfig,
) -> Result<Vec<ArchiveEntry>> {
let mut errs = vec![];
let mut entries = vec![];
let mut builder = CabinetBuilder::new();
let ctype = compression_type(config.level);
let folder = builder.add_folder(ctype);
let list = super::collect_entries(targets, config);
for path in list.iter() {
entries.push(ArchiveEntry::from(path));
folder.add_file(config.path_in_archive(path).to_str().unwrap());
}
let mut writer = match builder.build(file) {
Ok(w) => w,
Err(e) => return Err(Error::Archiver(e.to_string())),
};
for path in list.iter() {
if let Err(e) = write_entry(&mut writer, path) {
errs.push(e);
}
}
match writer.finish() {
Ok(_) => Ok(entries),
Err(e) => Err(Error::Archiver(e.to_string())),
}
}
fn enable(&self) -> bool {
true
}
}
fn compression_type(level: u8) -> cab::CompressionType {
match level {
0 => cab::CompressionType::None,
_ => cab::CompressionType::MsZip,
}
}
fn write_entry(writer: &mut CabinetWriter<File>, path: &Path) -> Result<()> {
match (File::open(path), writer.next_file()) {
(Ok(mut reader), Ok(Some(mut w))) => match std::io::copy(&mut reader, &mut w) {
Ok(_) => Ok(()),
Err(e) => Err(Error::IO(e)),
},
(_, Ok(None)) => Err(Error::Archiver("cab writer error".to_string())),
(Err(e1), Err(e2)) => Err(Error::Array(vec![
Error::IO(e1),
Error::Fatal(Box::new(e2)),
])),
(Err(e), _) => Err(Error::IO(e)),
(_, Err(e)) => Err(Error::Archiver(e.to_string())),
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
fn run_test<F>(f: F)
where
F: FnOnce(),
{
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
teardown();
if let Err(err) = result {
std::panic::resume_unwind(err);
}
}
#[test]
fn test_archive() {
run_test(|| {
let config = crate::ArchiveConfig::builder()
.dest("results/test.cab")
.overwrite(false)
.no_recursive(true)
.build();
let v = vec!["lib", "cli", "Cargo.toml"]
.into_iter()
.map(|s| PathBuf::from(s))
.collect::<Vec<_>>();
if let Err(e) = crate::archive(&v, &config) {
panic!("{e:?}")
}
});
}
fn teardown() {
let _ = std::fs::remove_file("results/test.cab");
}
}