use std::{fs::File, io::Read, path::Path};
use anyhow::{Context, Result};
use flate2::read::{DeflateDecoder, GzDecoder, MultiGzDecoder, ZlibDecoder};
use tar::Archive;
use xz2::read::XzDecoder;
use crate::common::{is_in_path, path_to_string, BSDTAR, SEVENZ};
use crate::io::{execute_and_output, execute_without_output};
use crate::{log_info, log_line};
pub fn decompress_zip(source: &Path) -> Result<()> {
let file = File::open(source)?;
let mut zip = zip::ZipArchive::new(file)?;
let parent = source
.parent()
.context("decompress: source should have a parent")?;
zip.extract(parent)?;
Ok(())
}
pub fn decompress_7z(source: &Path) -> Result<()> {
if !is_in_path(SEVENZ) {
log_info!(
"Can't decompress {source} without {SEVENZ} executable",
source = source.display()
);
log_line!(
"Can't decompress {source} without {SEVENZ} executable",
source = source.display()
);
return Ok(());
}
let parent = source
.parent()
.context("decompress: source should have a parent")?;
let args = &[
"x",
&path_to_string(&source),
&format!("-o{parent}", parent = parent.display()),
"-y",
"-bd",
];
let _ = execute_without_output(SEVENZ, args);
Ok(())
}
type Reader<'a> = &'a [fn(File) -> Box<dyn Read>];
pub fn decompress_xz_gz(source: &Path) -> Result<()> {
let parent = source
.parent()
.context("decompress: source should have a parent")?;
let strategies: Reader = &[
|f| Box::new(XzDecoder::new(f)),
|f| Box::new(GzDecoder::new(f)),
|f| Box::new(ZlibDecoder::new(f)),
|f| Box::new(DeflateDecoder::new(f)),
|f| Box::new(MultiGzDecoder::new(f)),
|f| Box::new(f),
];
for make_reader in strategies {
let file = File::open(source)?;
let reader = make_reader(file);
if try_unpack(reader, parent) {
return Ok(());
}
}
log_info!("Couldn't unpack the archive {}", source.display());
log_line!("Couldn't unpack the archive {}", source.display());
Ok(())
}
fn try_unpack<R: Read>(reader: R, dst: &Path) -> bool {
let mut archive = Archive::new(reader);
archive.unpack(dst).is_ok()
}
pub fn list_files_zip<P>(source: P) -> Result<Vec<String>>
where
P: AsRef<Path>,
{
let file = File::open(source)?;
let zip = zip::ZipArchive::new(file)?;
Ok(zip
.file_names()
.map(std::borrow::ToOwned::to_owned)
.collect())
}
pub fn list_files_tar<P>(source: P) -> Result<Vec<String>>
where
P: AsRef<Path>,
{
if let Ok(output) = execute_and_output(
BSDTAR,
["-v", "--list", "--file", path_to_string(&source).as_str()],
) {
let output = String::from_utf8(output.stdout).unwrap_or_default();
let content = output.lines().map(std::borrow::ToOwned::to_owned).collect();
return Ok(content);
}
Err(anyhow::anyhow!("Tar couldn't read the file content"))
}