From e08703850ce208cc1258e3516d32f7b93c465fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Rodrigues=20Miguel?= Date: Mon, 22 Mar 2021 04:46:54 -0300 Subject: [PATCH] Add support for decompressing .tar.{bz, xz, lz} and .zip.{bz, xz, lz} --- src/cli.rs | 9 +---- src/decompressors/decompressor.rs | 2 +- src/decompressors/niffler.rs | 2 +- src/decompressors/tar.rs | 19 +++++++--- src/decompressors/zip.rs | 62 ++++++++++++++++++++++--------- src/evaluator.rs | 41 +++++++++++++------- src/main.rs | 3 ++ src/test.rs | 18 ++++++++- src/utils.rs | 2 +- 9 files changed, 108 insertions(+), 50 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 80c1722..1da9d12 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -91,6 +91,7 @@ impl TryFrom> 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> 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 { diff --git a/src/decompressors/decompressor.rs b/src/decompressors/decompressor.rs index b6a7341..fc20ad2 100644 --- a/src/decompressors/decompressor.rs +++ b/src/decompressors/decompressor.rs @@ -8,5 +8,5 @@ pub enum DecompressionResult { } pub trait Decompressor { - fn decompress(&self, from: &File, into: &Option) -> OuchResult; + fn decompress(&self, from: File, into: &Option) -> OuchResult; } \ No newline at end of file diff --git a/src/decompressors/niffler.rs b/src/decompressors/niffler.rs index d51be06..3539c61 100644 --- a/src/decompressors/niffler.rs +++ b/src/decompressors/niffler.rs @@ -42,7 +42,7 @@ impl NifflerDecompressor { } impl Decompressor for NifflerDecompressor { - fn decompress(&self, from: &File, into: &Option) -> OuchResult { + fn decompress(&self, from: File, into: &Option) -> OuchResult { let destination_path = utils::get_destination_path(into); utils::create_path_if_non_existent(destination_path)?; diff --git a/src/decompressors/tar.rs b/src/decompressors/tar.rs index eee2f9d..5b23e9d 100644 --- a/src/decompressors/tar.rs +++ b/src/decompressors/tar.rs @@ -12,13 +12,20 @@ pub struct TarDecompressor {} impl TarDecompressor { - fn unpack_files(from: &File, into: &Path) -> OuchResult> { + fn unpack_files(from: File, into: &Path) -> OuchResult> { - 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> = 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) -> OuchResult { + fn decompress(&self, from: File, into: &Option) -> OuchResult { 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)) } diff --git a/src/decompressors/zip.rs b/src/decompressors/zip.rs index c7f5fff..7eb32d9 100644 --- a/src/decompressors/zip.rs +++ b/src/decompressors/zip.rs @@ -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> { - + pub fn zip_decompress( + archive: &mut ZipArchive, + into: &Path, + ) -> error::OuchResult> + 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> { + + 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) -> OuchResult { + fn decompress(&self, from: File, into: &Option) -> OuchResult { 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)) } -} \ No newline at end of file +} diff --git a/src/evaluator.rs b/src/evaluator.rs index 9016535..dbfe03b 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -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 { match ext { @@ -58,33 +57,46 @@ impl Evaluator { // todo: move this folder into decompressors/ later on fn decompress_file_in_memory( bytes: Vec, - file: &File, + file_path: PathBuf, decompressor: Option>, output_file: &Option, + extension: Option, ) -> 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. diff --git a/src/main.rs b/src/main.rs index e873960..511aef2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,5 +30,8 @@ fn main() -> OuchResult<()>{ print_error(err) } } + + // let extension = dbg!(Extension::new("file.tar.gz")); + Ok(()) } diff --git a/src/test.rs b/src/test.rs index 7703215..64fcbbc 100644 --- a/src/test.rs +++ b/src/test.rs @@ -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<()> { diff --git a/src/utils.rs b/src/utils.rs index e6d1d1f..82babb9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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};