Add support for Lzma decompression

This commit is contained in:
Vinícius Rodrigues Miguel 2021-03-24 01:40:16 -03:00
parent e18dbbd667
commit 729dda819e
12 changed files with 116 additions and 59 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target
/tests

View File

@ -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" }
zip = { version = "0.5.10", path = "./third-party/zip" }

View File

@ -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?

View File

@ -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),
)
}

45
src/decompressors/lzma.rs Normal file
View File

@ -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<Vec<u8>> {
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<File>) -> OuchResult<DecompressionResult> {
let destination_path = utils::get_destination_path(into);
utils::create_path_if_non_existent(destination_path)?;
Ok(
DecompressionResult::FileInMemory(
Self::extract_to_memory(from)?
)
)
}
}

View File

@ -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;
pub use self::lzma::LzmaDecompressor;

View File

@ -8,6 +8,7 @@ use crate::file::File;
use super::decompressor::{DecompressionResult, Decompressor};
#[derive(Debug)]
pub struct TarDecompressor {}
impl TarDecompressor {

View File

@ -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<dyn io::Read + Send + 'a>) -> Box<dyn io::Read + Send + 'a> {
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<Vec<u8>> {
// 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<File>) -> OuchResult<DecompressionResult> {
UnifiedDecompressor::decompress(from, CompressionFormat::Lzma, into)
}
}
impl Decompressor for GzipDecompressor {
fn decompress(&self, from: File, into: &Option<File>) -> OuchResult<DecompressionResult> {
UnifiedDecompressor::decompress(from, CompressionFormat::Gzip, into)

View File

@ -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)

View File

@ -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<dyn Compressor>>, Box<dyn Compressor>)> {
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<dyn Decompressor>>, Box<dyn Decompressor>)> {
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 {})

View File

@ -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(())
// }

View File

@ -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(())
// }
// }