From 729dda819e48532a952f475da129748daff48a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Rodrigues=20Miguel?= Date: Wed, 24 Mar 2021 01:40:16 -0300 Subject: [PATCH] Add support for Lzma decompression --- .gitignore | 1 + Cargo.toml | 4 ++-- README.md | 4 ++-- src/cli.rs | 11 +++++++-- src/decompressors/lzma.rs | 45 ++++++++++++++++++++++++++++++++++++ src/decompressors/mod.rs | 6 +++-- src/decompressors/tar.rs | 1 + src/decompressors/unified.rs | 18 ++++----------- src/error.rs | 7 +----- src/evaluator.rs | 16 +++++-------- src/main.rs | 31 ++++++++----------------- src/test.rs | 31 +++++++++++++++++++++++++ 12 files changed, 116 insertions(+), 59 deletions(-) create mode 100644 src/decompressors/lzma.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..54b52f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/tests diff --git a/Cargo.toml b/Cargo.toml index 9a8c4d1..af1f663 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,11 +19,11 @@ colored = "2.0.0" walkdir = "2.3.2" clap = "2.33.3" tar = "0.4.33" -xz2 = "0.1" +xz2 = "0.1.6" bzip2 = "0.4.2" flate2 = "1.0.20" # Keeping zip locally since upstream zip is staying on an older flate2 version # in order to not increase MSRV, which is not something I particularly care about # for ouch -zip = { path = "./third-party/zip" } \ No newline at end of file +zip = { version = "0.5.10", path = "./third-party/zip" } diff --git a/README.md b/README.md index 8f1dc6f..720069f 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ `ouch` is the Obvious Unified Compression (and decompression) Helper. -| Supported formats | .tar | .zip | .tar.{.gz, .bz} | .zip.{.gz, .bz, .bz2} | .bz | .gz | .lz, .lzma | +| Supported formats | .tar | .zip | .tar.{.lz,.gz, .bz} | .zip.{.lz, .gz, .bz, .bz2} | .bz | .gz | .lz, .lzma | |-------------------|------|------|------------------------------|------------------------------|-----|-----|------------| -| Decompression | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | +| Decompression | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Compression | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ## How does it work? diff --git a/src/cli.rs b/src/cli.rs index c168667..fbde865 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -29,6 +29,13 @@ pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> { clap::App::new("ouch") .version("0.1.0") .about("ouch is a unified compression & decompression utility") + .after_help( +"ouch infers what to based on the extensions of the input files and output file received. +Examples: `ouch -i movies.tar.gz classes.zip -o Videos/` in order to decompress files into a folder. + `ouch -i headers/ sources/ Makefile -o my-project.tar.gz` + `ouch -i image{1..50}.jpeg -o images.zip` +Please relate any issues or contribute at https://github.com/vrmiguel/ouch") + .author("Vinícius R. Miguel") .help_message("Displays this message and exits") .settings(&[ clap::AppSettings::ColoredHelp, @@ -40,7 +47,7 @@ pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> { .multiple(true) .long("input") .short("i") - .help("Input files (TODO description)") + .help("The input files or directories.") .takes_value(true), ) .arg( @@ -50,7 +57,7 @@ pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> { .multiple(false) .long("output") .short("o") - .help("Output file (TODO description)") + .help("The output directory or compressed file.") .takes_value(true), ) } diff --git a/src/decompressors/lzma.rs b/src/decompressors/lzma.rs new file mode 100644 index 0000000..a970feb --- /dev/null +++ b/src/decompressors/lzma.rs @@ -0,0 +1,45 @@ +use std::{fs, io::Read}; + +use colored::Colorize; + +use crate::{error::OuchResult, utils}; +use crate::file::File; + +use super::decompressor::{DecompressionResult, Decompressor}; + +pub struct LzmaDecompressor {} + +impl LzmaDecompressor { + fn extract_to_memory(from: File) -> OuchResult> { + let mut ret = vec![]; + + let from_path = from.path; + if !from_path.exists() { + eprintln!("{}: could not find {:?}", "error".red(), from_path); + } + + let input_bytes = fs::read(&from_path)?; + + + xz2::read::XzDecoder::new_multi_decoder(&*input_bytes) + .read_to_end(&mut ret)?; + + println!("{}: extracted {:?} into memory. ({} bytes)", "info".yellow(), from_path, ret.len()); + + Ok(ret) + } +} + +impl Decompressor for LzmaDecompressor { + fn decompress(&self, from: File, into: &Option) -> OuchResult { + let destination_path = utils::get_destination_path(into); + + utils::create_path_if_non_existent(destination_path)?; + + Ok( + DecompressionResult::FileInMemory( + Self::extract_to_memory(from)? + ) + ) + } +} \ No newline at end of file diff --git a/src/decompressors/mod.rs b/src/decompressors/mod.rs index b500678..5b9c1b0 100644 --- a/src/decompressors/mod.rs +++ b/src/decompressors/mod.rs @@ -1,7 +1,9 @@ mod decompressor; +mod unified; +mod lzma; mod tar; mod zip; -mod unified; + pub use decompressor::Decompressor; pub use decompressor::DecompressionResult; @@ -9,4 +11,4 @@ pub use self::tar::TarDecompressor; pub use self::zip::ZipDecompressor; pub use self::unified::GzipDecompressor; pub use self::unified::BzipDecompressor; -pub use self::unified::LzmaDecompressor; \ No newline at end of file +pub use self::lzma::LzmaDecompressor; \ No newline at end of file diff --git a/src/decompressors/tar.rs b/src/decompressors/tar.rs index 7e11d6e..0c6e2a4 100644 --- a/src/decompressors/tar.rs +++ b/src/decompressors/tar.rs @@ -8,6 +8,7 @@ use crate::file::File; use super::decompressor::{DecompressionResult, Decompressor}; +#[derive(Debug)] pub struct TarDecompressor {} impl TarDecompressor { diff --git a/src/decompressors/unified.rs b/src/decompressors/unified.rs index c116be1..463270e 100644 --- a/src/decompressors/unified.rs +++ b/src/decompressors/unified.rs @@ -1,15 +1,15 @@ use std::{ io::{self, Read}, - path::{Path, PathBuf}, + path::Path, }; -use bzip2::Compress; + use colored::Colorize; // use niffler; use crate::{extension::CompressionFormat, file::File}; use crate::{ - error::{self, OuchResult}, + error::OuchResult, utils, }; @@ -17,23 +17,19 @@ use super::decompressor::DecompressionResult; use super::decompressor::Decompressor; pub struct UnifiedDecompressor {} -pub struct LzmaDecompressor {} pub struct GzipDecompressor {} pub struct BzipDecompressor {} fn get_decoder<'a>(format: CompressionFormat, buffer: Box) -> Box { match format { - CompressionFormat::Lzma => Box::new(xz2::read::XzDecoder::new(buffer)), CompressionFormat::Bzip => Box::new(bzip2::read::BzDecoder::new(buffer)), CompressionFormat::Gzip => Box::new(flate2::read::MultiGzDecoder::new(buffer)), - other => unreachable!() + _other => unreachable!() } } impl UnifiedDecompressor { fn unpack_file(from: &Path, format: CompressionFormat) -> OuchResult> { - // println!("{}: trying to decompress {:?}", "info".yellow(), from); - let file = std::fs::read(from)?; let mut reader = get_decoder(format, Box::new(&file[..])); @@ -62,12 +58,6 @@ impl UnifiedDecompressor { } } -impl Decompressor for LzmaDecompressor { - fn decompress(&self, from: File, into: &Option) -> OuchResult { - UnifiedDecompressor::decompress(from, CompressionFormat::Lzma, into) - } -} - impl Decompressor for GzipDecompressor { fn decompress(&self, from: File, into: &Option) -> OuchResult { UnifiedDecompressor::decompress(from, CompressionFormat::Gzip, into) diff --git a/src/error.rs b/src/error.rs index 008393b..c3b37bc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use std::{fmt, path::PathBuf}; +use std::fmt; use colored::Colorize; @@ -13,10 +13,8 @@ pub enum Error { FileNotFound, AlreadyExists, InvalidZipArchive(&'static str), - UnsupportedArchive(PathBuf), PermissionDenied, UnsupportedZipArchive(&'static str), - FileTooShort, InputsMustHaveBeenDecompressible(String), } @@ -40,9 +38,6 @@ impl fmt::Display for Error { Error::FileNotFound => { write!(f, "file not found!") } - Error::UnsupportedArchive(path) => { - write!(f, "ouch is currently uncapable of decompressing {:?}", path) - } err => { // TODO write!(f, "todo: missing description for error {:?}", err) diff --git a/src/evaluator.rs b/src/evaluator.rs index 1eb1be5..6a2d22f 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -15,6 +15,7 @@ use crate::decompressors::{ ZipDecompressor, GzipDecompressor, BzipDecompressor, + LzmaDecompressor, DecompressionResult }; @@ -31,12 +32,10 @@ use crate::file::File; use crate::utils; -pub struct Evaluator { - // verbosity: Verbosity -} +pub struct Evaluator {} impl Evaluator { - fn get_compressor( + pub fn get_compressor( file: &File, ) -> error::OuchResult<(Option>, Box)> { if file.extension.is_none() { @@ -71,13 +70,12 @@ impl Evaluator { CompressionFormat::Tar => Box::new(TarCompressor {}), CompressionFormat::Zip => Box::new(ZipCompressor {}), _other => todo!() - // }; Ok((first_compressor, second_compressor)) } - fn get_decompressor( + pub fn get_decompressor( file: &File, ) -> error::OuchResult<(Option>, Box)> { if file.extension.is_none() { @@ -95,11 +93,9 @@ impl Evaluator { CompressionFormat::Zip => Box::new(ZipDecompressor {}), - CompressionFormat::Gzip => Box::new(GzipDecompressor {}), + CompressionFormat::Gzip => Box::new(GzipDecompressor{}), - CompressionFormat::Lzma => { - todo!() - } + CompressionFormat::Lzma => Box::new(LzmaDecompressor{}), CompressionFormat::Bzip => { Box::new(BzipDecompressor {}) diff --git a/src/main.rs b/src/main.rs index 02c4d88..eabeafb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ -use std::{convert::TryFrom, io::Write}; +use std::convert::TryFrom; use colored::Colorize; -use walkdir::WalkDir; mod cli; mod error; @@ -34,27 +33,17 @@ fn main() -> error::OuchResult<()>{ Ok(()) } -// fn main() { -// use zip::ZipWriter; +// fn main() -> error::OuchResult<()> { +// let bytes = fs::read("extension.tar.lzma")?; -// let buf = vec![]; -// let mut writer = zip::ZipWriter::new(std::io::Cursor::new(buf)); +// let mut ret = vec![]; -// let options = zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated); +// xz2::read::XzDecoder::new_multi_decoder(&*bytes) +// .read_to_end(&mut ret) +// .unwrap(); + +// fs::write("extension.tar", &*bytes).unwrap(); -// for entry in WalkDir::new("src/compressors/compressor.rs") { -// let entry = entry.unwrap(); -// let entry_path = entry.path().clone(); -// if entry_path.is_dir() { -// continue; -// } -// writer.start_file(entry_path.to_string_lossy(), options).unwrap(); -// let file_bytes = std::fs::read(entry.path()).unwrap(); -// writer.write(&*file_bytes).unwrap(); -// } - -// let bytes = writer.finish().unwrap(); - -// std::fs::write("mainmain.rar", bytes.into_inner()).unwrap(); +// Ok(()) // } \ No newline at end of file diff --git a/src/test.rs b/src/test.rs index 1aed192..4e61217 100644 --- a/src/test.rs +++ b/src/test.rs @@ -201,3 +201,34 @@ mod extension_extraction { Ok(()) } } + +// #[cfg(test)] +// mod evaluator { +// use crate::extension::Extension; +// use crate::error::OuchResult; +// use crate::file::File; +// use crate::evaluator::Evaluator; +// use crate::decompressors::{Decompressor, TarDecompressor, GzipDecompressor}; + +// #[test] +// fn test() -> OuchResult<()> { +// let extension = Extension::new("folder.tar.gz")?; + +// let file = File { +// path: "folder.tar.gz".into(), +// contents_in_memory: None, +// extension: Some(extension), +// }; + +// let (fst, snd) = Evaluator::get_decompressor(&file)?; + +// let fst = fst.unwrap(); + +// assert_eq!( +// fst, +// Some(Box::new(TarDecompressor::{}) +// ); + +// Ok(()) +// } +// } \ No newline at end of file