use std::fs::File;
use std::path::PathBuf;
use crate::{Result, Error};
use chrono::DateTime;
use sevenz_rust::{Archive, BlockDecoder, Password, SevenZArchiveEntry};
use crate::extractor::{Entry, Entries, ToteExtractor};
pub(super) struct Extractor {}
impl ToteExtractor for Extractor {
fn list(&self, archive_file: PathBuf) -> Result<Entries> {
let mut reader = File::open(&archive_file).unwrap();
let len = reader.metadata().unwrap().len();
match Archive::read(&mut reader, len, Password::empty().as_ref()) {
Ok(archive) => {
let mut r = vec![];
for entry in &archive.files {
r.push(convert(entry));
}
Ok(Entries::new(archive_file, r))
}
Err(e) => Err(Error::Extractor(e.to_string())),
}
}
fn perform(&self, archive_file: PathBuf, base: PathBuf) -> Result<()> {
let file = File::open(archive_file)
.map_err(Error::IO)?;
extract(&file, base)
}
}
fn convert(e: &SevenZArchiveEntry) -> Entry {
let name = e.name().to_string();
let compressed_size = e.compressed_size;
let uncompressed_size = e.size;
let mtime = e.last_modified_date.to_unix_time();
let dt = DateTime::from_timestamp(mtime, 0);
Entry::builder()
.name(name)
.compressed_size(compressed_size)
.original_size(uncompressed_size)
.date(dt.map(|dt| dt.naive_local()))
.build()
}
fn extract(mut file: &File, base: PathBuf) -> Result<()> {
let len = file.metadata().unwrap().len();
let password = Password::empty();
let archive = match Archive::read(&mut file, len, password.as_ref()) {
Ok(reader) => reader,
Err(e) => return Err(Error::Fatal(Box::new(e))),
};
let folder_count = archive.folders.len();
for findex in 0..folder_count {
let folder_decoder = BlockDecoder::new(findex, &archive, password.as_slice(), &mut file);
if let Err(e) = folder_decoder.for_each_entries(&mut |entry, reader| {
let d = base.join(&entry.name);
sevenz_rust::default_entry_extract_fn(entry, reader, &d)
}) {
return Err(Error::Fatal(Box::new(e)));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_list() {
let file = PathBuf::from("../testdata/test.7z");
let extractor = Extractor {};
match extractor.list(file) {
Ok(r) => {
let r = r.iter().map(|e| e.name.clone()).collect::<Vec<_>>();
assert_eq!(r.len(), 21);
assert_eq!(r.get(0), Some("Cargo.toml".to_string()).as_ref());
assert_eq!(r.get(1), Some("build.rs".to_string()).as_ref());
assert_eq!(r.get(2), Some("LICENSE".to_string()).as_ref());
assert_eq!(r.get(3), Some("README.md".to_string()).as_ref());
}
Err(_) => assert!(false),
}
}
#[test]
fn test_extract_archive() {
let archive_file = PathBuf::from("../testdata/test.7z");
let opts = crate::ExtractConfig::builder()
.dest("results/sevenz")
.build();
match crate::extract(archive_file, &opts) {
Ok(_) => {
assert!(true);
assert!(PathBuf::from("results/sevenz/Cargo.toml").exists());
std::fs::remove_dir_all(PathBuf::from("results/sevenz")).unwrap();
}
Err(_) => assert!(false),
};
}
}