Add support for decompressing .tar.{bz, xz, lz} and .zip.{bz, xz, lz}

This commit is contained in:
Vinícius Rodrigues Miguel 2021-03-22 04:46:54 -03:00
parent 77d7613967
commit e08703850c
9 changed files with 108 additions and 50 deletions

View File

@ -91,6 +91,7 @@ impl TryFrom<clap::ArgMatches<'static>> for Command {
let output_file = matches.value_of("output").unwrap(); // Safe unwrap since we've established that output was supplied
let output_file_extension = Extension::new(output_file);
let output_is_compressible = output_file_extension.is_ok();
if output_is_compressible {
// The supplied output is compressible, so we'll compress our inputs to it
@ -103,14 +104,6 @@ impl TryFrom<clap::ArgMatches<'static>> for Command {
let input_files = input_files.map(PathBuf::from).collect();
// return Ok(Command {
// kind: CommandKind::Compression(input_files),
// output: Some(File::WithExtension((
// output_file.into(),
// output_file_extension.unwrap(),
// ))),
// });
return Ok(Command {
kind: CommandKind::Compression(input_files),
output: Some(File {

View File

@ -8,5 +8,5 @@ pub enum DecompressionResult {
}
pub trait Decompressor {
fn decompress(&self, from: &File, into: &Option<File>) -> OuchResult<DecompressionResult>;
fn decompress(&self, from: File, into: &Option<File>) -> OuchResult<DecompressionResult>;
}

View File

@ -42,7 +42,7 @@ impl NifflerDecompressor {
}
impl Decompressor for NifflerDecompressor {
fn decompress(&self, from: &File, into: &Option<File>) -> OuchResult<DecompressionResult> {
fn decompress(&self, from: File, into: &Option<File>) -> OuchResult<DecompressionResult> {
let destination_path = utils::get_destination_path(into);
utils::create_path_if_non_existent(destination_path)?;

View File

@ -12,13 +12,20 @@ pub struct TarDecompressor {}
impl TarDecompressor {
fn unpack_files(from: &File, into: &Path) -> OuchResult<Vec<PathBuf>> {
fn unpack_files(from: File, into: &Path) -> OuchResult<Vec<PathBuf>> {
println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), from);
println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), &from.path);
let mut files_unpacked = vec![];
let file = fs::File::open(&from.path)?;
let mut archive = tar::Archive::new(file);
let mut archive: Archive<Box<dyn Read>> = match from.contents {
Some(bytes) => {
tar::Archive::new(Box::new(Cursor::new(bytes)))
}
None => {
let file = fs::File::open(&from.path)?;
tar::Archive::new(Box::new(file))
}
};
for file in archive.entries()? {
let mut file = file?;
@ -42,12 +49,12 @@ impl TarDecompressor {
}
impl Decompressor for TarDecompressor {
fn decompress(&self, from: &File, into: &Option<File>) -> OuchResult<DecompressionResult> {
fn decompress(&self, from: File, into: &Option<File>) -> OuchResult<DecompressionResult> {
let destination_path = utils::get_destination_path(into);
utils::create_path_if_non_existent(destination_path)?;
let files_unpacked = Self::unpack_files(&from, destination_path)?;
let files_unpacked = Self::unpack_files(from, destination_path)?;
Ok(DecompressionResult::FilesUnpacked(files_unpacked))
}

View File

@ -1,33 +1,36 @@
use std::{fs, io, path::{Path, PathBuf}};
use std::{fs, io::{self, Cursor, Read, Seek}, path::{Path, PathBuf}};
use colored::Colorize;
use zip::{self, read::ZipFile};
use zip::{self, ZipArchive, read::ZipFile};
use crate::{error::{self, OuchResult}, utils};
use crate::file::File;
use crate::{error, file::File};
use crate::{error::OuchResult, utils};
use super::decompressor::{DecompressionResult, Decompressor};
pub struct ZipDecompressor {}
impl ZipDecompressor {
fn check_for_comments(file: &ZipFile) {
let comment = file.comment();
if !comment.is_empty() {
println!("{}: Comment in {}: {}", "info".yellow(), file.name(), comment);
println!(
"{}: Comment in {}: {}",
"info".yellow(),
file.name(),
comment
);
}
}
fn unpack_files(from: &Path, into: &Path) -> OuchResult<Vec<PathBuf>> {
pub fn zip_decompress<T>(
archive: &mut ZipArchive<T>,
into: &Path,
) -> error::OuchResult<Vec<PathBuf>>
where
T: Read + Seek,
{
let mut unpacked_files = vec![];
println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), from);
let file = fs::File::open(from)?;
let mut archive = zip::ZipArchive::new(file)?;
for idx in 0..archive.len() {
let mut file = archive.by_index(idx)?;
let file_path = match file.enclosed_name() {
@ -68,17 +71,40 @@ impl ZipDecompressor {
Ok(unpacked_files)
}
fn unpack_files(from: File, into: &Path) -> OuchResult<Vec<PathBuf>> {
println!(
"{}: attempting to decompress {:?}",
"ouch".bright_blue(),
from
);
match from.contents {
Some(bytes) => {
let mut archive = zip::ZipArchive::new(Cursor::new(bytes))?;
Ok(Self::zip_decompress(&mut archive, into)?)
},
None => {
let file = fs::File::open(&from.path)?;
let mut archive = zip::ZipArchive::new(file)?;
Ok(Self::zip_decompress(&mut archive, into)?)
}
}
}
}
impl Decompressor for ZipDecompressor {
fn decompress(&self, from: &File, into: &Option<File>) -> OuchResult<DecompressionResult> {
fn decompress(&self, from: File, into: &Option<File>) -> OuchResult<DecompressionResult> {
let destination_path = utils::get_destination_path(into);
utils::create_path_if_non_existent(destination_path)?;
let files_unpacked = Self::unpack_files(&from.path, destination_path)?;
let files_unpacked = Self::unpack_files(from, destination_path)?;
Ok(DecompressionResult::FilesUnpacked(files_unpacked))
}
}
}

View File

@ -1,8 +1,8 @@
use std::{ffi::OsStr, fs, io::Write};
use std::{ffi::OsStr, fs, io::Write, path::PathBuf};
use colored::Colorize;
use crate::decompressors::Decompressor;
use crate::{decompressors::Decompressor, extension::Extension};
use crate::decompressors::TarDecompressor;
use crate::decompressors::ZipDecompressor;
use crate::{
@ -15,7 +15,6 @@ use crate::{
};
pub struct Evaluator {
command: Command,
// verbosity: Verbosity
}
@ -31,7 +30,7 @@ impl Evaluator {
);
return Err(error::Error::InvalidInput);
}
let extension = file.extension.clone().unwrap();
let extension = Extension::new(&file.path.to_str().unwrap())?;
let decompressor_from_format = |ext| -> Box<dyn Decompressor> {
match ext {
@ -58,33 +57,46 @@ impl Evaluator {
// todo: move this folder into decompressors/ later on
fn decompress_file_in_memory(
bytes: Vec<u8>,
file: &File,
file_path: PathBuf,
decompressor: Option<Box<dyn Decompressor>>,
output_file: &Option<File>,
extension: Option<Extension>,
) -> OuchResult<()> {
let output_file = utils::get_destination_path(output_file);
let output_file_path = utils::get_destination_path(output_file);
let mut filename = file.path.file_stem().unwrap_or(output_file.as_os_str());
let mut filename = file_path.file_stem().unwrap_or(output_file_path.as_os_str());
if filename == "." {
// I believe this is only possible when the supplied inout has a name
// of the sort `.tar` or `.zip' and no output has been supplied.
filename = OsStr::new("ouch-output");
}
let filename = PathBuf::from(filename);
if decompressor.is_none() {
// There is no more processing to be done on the input file (or there is but currently unsupported)
// Therefore, we'll save what we have in memory into a file.
println!("{}: saving to {:?}.", "info".yellow(), filename);
let mut f = fs::File::create(output_file.join(filename))?;
let mut f = fs::File::create(output_file_path.join(filename))?;
f.write_all(&bytes)?;
return Ok(());
}
// If there is a decompressor to use, we'll create a file in-memory (to-do) and decompress it
// TODO: change decompressor logic to use BufReader or something like that
let file = File {
path: filename,
contents: Some(bytes),
extension,
};
let decompressor = decompressor.unwrap();
// If there is a decompressor to use, we'll create a file in-memory and decompress it
let decompression_result = decompressor.decompress(file, output_file)?;
Ok(())
}
@ -93,15 +105,18 @@ impl Evaluator {
// let output_file = &command.output;
let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?;
let decompression_result = second_decompressor.decompress(&file, output)?;
let file_path = file.path.clone();
let extension = file.extension.clone();
let decompression_result = second_decompressor.decompress(file, output)?;
match decompression_result {
DecompressionResult::FileInMemory(bytes) => {
// We'll now decompress a file currently in memory.
// This will currently happen in the case of .bz, .xz and .lzma
Self::decompress_file_in_memory(bytes, &file, first_decompressor, output)?;
Self::decompress_file_in_memory(bytes, file_path, first_decompressor, output, extension)?;
}
DecompressionResult::FilesUnpacked(files) => {
DecompressionResult::FilesUnpacked(_files) => {
// If the file's last extension was an archival method,
// such as .tar, .zip or (to-do) .rar, then we won't look for
// further processing.

View File

@ -30,5 +30,8 @@ fn main() -> OuchResult<()>{
print_error(err)
}
}
// let extension = dbg!(Extension::new("file.tar.gz"));
Ok(())
}

View File

@ -84,7 +84,6 @@ mod cli {
"file2.jpeg".into(),
"file3.ok".into()
]),
// output: Some(File::WithExtension(("file.tar".into(), Extension::from(Tar))))
output: Some(
File {
path: "file.tar".into(),
@ -126,7 +125,7 @@ mod cli_errors {
#[cfg(test)]
mod extension_extraction {
use crate::error::OuchResult;
use crate::{error::OuchResult, extension::Extension};
use crate::extension::CompressionFormat;
use std::{convert::TryFrom, path::PathBuf, str::FromStr};
@ -140,6 +139,21 @@ mod extension_extraction {
Ok(())
}
#[test]
fn tar_gz() -> OuchResult<()> {
let extension = Extension::new("folder.tar.gz")?;
assert_eq!(
extension,
Extension {
first_ext: Some(CompressionFormat::Tar),
second_ext: CompressionFormat::Gzip
}
);
Ok(())
}
#[test]
fn tar() -> OuchResult<()> {

View File

@ -1,4 +1,4 @@
use std::{fs, path::{Component, Path, PathBuf}};
use std::{fs, path::Path};
use colored::Colorize;
use crate::{error::OuchResult, file::File};