mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-06 11:35:45 +00:00
Add support for Lzma decompression
This commit is contained in:
parent
e18dbbd667
commit
729dda819e
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/tests
|
||||||
|
@ -19,11 +19,11 @@ colored = "2.0.0"
|
|||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
tar = "0.4.33"
|
tar = "0.4.33"
|
||||||
xz2 = "0.1"
|
xz2 = "0.1.6"
|
||||||
bzip2 = "0.4.2"
|
bzip2 = "0.4.2"
|
||||||
flate2 = "1.0.20"
|
flate2 = "1.0.20"
|
||||||
|
|
||||||
# Keeping zip locally since upstream zip is staying on an older flate2 version
|
# 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
|
# in order to not increase MSRV, which is not something I particularly care about
|
||||||
# for ouch
|
# for ouch
|
||||||
zip = { path = "./third-party/zip" }
|
zip = { version = "0.5.10", path = "./third-party/zip" }
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
`ouch` is the Obvious Unified Compression (and decompression) Helper.
|
`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 | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
|
| Compression | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
|
||||||
|
|
||||||
## How does it work?
|
## How does it work?
|
||||||
|
11
src/cli.rs
11
src/cli.rs
@ -29,6 +29,13 @@ pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
clap::App::new("ouch")
|
clap::App::new("ouch")
|
||||||
.version("0.1.0")
|
.version("0.1.0")
|
||||||
.about("ouch is a unified compression & decompression utility")
|
.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")
|
.help_message("Displays this message and exits")
|
||||||
.settings(&[
|
.settings(&[
|
||||||
clap::AppSettings::ColoredHelp,
|
clap::AppSettings::ColoredHelp,
|
||||||
@ -40,7 +47,7 @@ pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.multiple(true)
|
.multiple(true)
|
||||||
.long("input")
|
.long("input")
|
||||||
.short("i")
|
.short("i")
|
||||||
.help("Input files (TODO description)")
|
.help("The input files or directories.")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -50,7 +57,7 @@ pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.multiple(false)
|
.multiple(false)
|
||||||
.long("output")
|
.long("output")
|
||||||
.short("o")
|
.short("o")
|
||||||
.help("Output file (TODO description)")
|
.help("The output directory or compressed file.")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
45
src/decompressors/lzma.rs
Normal file
45
src/decompressors/lzma.rs
Normal 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)?
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
mod decompressor;
|
mod decompressor;
|
||||||
|
mod unified;
|
||||||
|
mod lzma;
|
||||||
mod tar;
|
mod tar;
|
||||||
mod zip;
|
mod zip;
|
||||||
mod unified;
|
|
||||||
|
|
||||||
pub use decompressor::Decompressor;
|
pub use decompressor::Decompressor;
|
||||||
pub use decompressor::DecompressionResult;
|
pub use decompressor::DecompressionResult;
|
||||||
@ -9,4 +11,4 @@ pub use self::tar::TarDecompressor;
|
|||||||
pub use self::zip::ZipDecompressor;
|
pub use self::zip::ZipDecompressor;
|
||||||
pub use self::unified::GzipDecompressor;
|
pub use self::unified::GzipDecompressor;
|
||||||
pub use self::unified::BzipDecompressor;
|
pub use self::unified::BzipDecompressor;
|
||||||
pub use self::unified::LzmaDecompressor;
|
pub use self::lzma::LzmaDecompressor;
|
@ -8,6 +8,7 @@ use crate::file::File;
|
|||||||
|
|
||||||
use super::decompressor::{DecompressionResult, Decompressor};
|
use super::decompressor::{DecompressionResult, Decompressor};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TarDecompressor {}
|
pub struct TarDecompressor {}
|
||||||
|
|
||||||
impl TarDecompressor {
|
impl TarDecompressor {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
path::{Path, PathBuf},
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bzip2::Compress;
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
// use niffler;
|
// use niffler;
|
||||||
|
|
||||||
use crate::{extension::CompressionFormat, file::File};
|
use crate::{extension::CompressionFormat, file::File};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{self, OuchResult},
|
error::OuchResult,
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,23 +17,19 @@ use super::decompressor::DecompressionResult;
|
|||||||
use super::decompressor::Decompressor;
|
use super::decompressor::Decompressor;
|
||||||
|
|
||||||
pub struct UnifiedDecompressor {}
|
pub struct UnifiedDecompressor {}
|
||||||
pub struct LzmaDecompressor {}
|
|
||||||
pub struct GzipDecompressor {}
|
pub struct GzipDecompressor {}
|
||||||
pub struct BzipDecompressor {}
|
pub struct BzipDecompressor {}
|
||||||
|
|
||||||
fn get_decoder<'a>(format: CompressionFormat, buffer: Box<dyn io::Read + Send + 'a>) -> Box<dyn io::Read + Send + 'a> {
|
fn get_decoder<'a>(format: CompressionFormat, buffer: Box<dyn io::Read + Send + 'a>) -> Box<dyn io::Read + Send + 'a> {
|
||||||
match format {
|
match format {
|
||||||
CompressionFormat::Lzma => Box::new(xz2::read::XzDecoder::new(buffer)),
|
|
||||||
CompressionFormat::Bzip => Box::new(bzip2::read::BzDecoder::new(buffer)),
|
CompressionFormat::Bzip => Box::new(bzip2::read::BzDecoder::new(buffer)),
|
||||||
CompressionFormat::Gzip => Box::new(flate2::read::MultiGzDecoder::new(buffer)),
|
CompressionFormat::Gzip => Box::new(flate2::read::MultiGzDecoder::new(buffer)),
|
||||||
other => unreachable!()
|
_other => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnifiedDecompressor {
|
impl UnifiedDecompressor {
|
||||||
fn unpack_file(from: &Path, format: CompressionFormat) -> OuchResult<Vec<u8>> {
|
fn unpack_file(from: &Path, format: CompressionFormat) -> OuchResult<Vec<u8>> {
|
||||||
// println!("{}: trying to decompress {:?}", "info".yellow(), from);
|
|
||||||
|
|
||||||
let file = std::fs::read(from)?;
|
let file = std::fs::read(from)?;
|
||||||
|
|
||||||
let mut reader = get_decoder(format, Box::new(&file[..]));
|
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 {
|
impl Decompressor for GzipDecompressor {
|
||||||
fn decompress(&self, from: File, into: &Option<File>) -> OuchResult<DecompressionResult> {
|
fn decompress(&self, from: File, into: &Option<File>) -> OuchResult<DecompressionResult> {
|
||||||
UnifiedDecompressor::decompress(from, CompressionFormat::Gzip, into)
|
UnifiedDecompressor::decompress(from, CompressionFormat::Gzip, into)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{fmt, path::PathBuf};
|
use std::fmt;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
@ -13,10 +13,8 @@ pub enum Error {
|
|||||||
FileNotFound,
|
FileNotFound,
|
||||||
AlreadyExists,
|
AlreadyExists,
|
||||||
InvalidZipArchive(&'static str),
|
InvalidZipArchive(&'static str),
|
||||||
UnsupportedArchive(PathBuf),
|
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
UnsupportedZipArchive(&'static str),
|
UnsupportedZipArchive(&'static str),
|
||||||
FileTooShort,
|
|
||||||
InputsMustHaveBeenDecompressible(String),
|
InputsMustHaveBeenDecompressible(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,9 +38,6 @@ impl fmt::Display for Error {
|
|||||||
Error::FileNotFound => {
|
Error::FileNotFound => {
|
||||||
write!(f, "file not found!")
|
write!(f, "file not found!")
|
||||||
}
|
}
|
||||||
Error::UnsupportedArchive(path) => {
|
|
||||||
write!(f, "ouch is currently uncapable of decompressing {:?}", path)
|
|
||||||
}
|
|
||||||
err => {
|
err => {
|
||||||
// TODO
|
// TODO
|
||||||
write!(f, "todo: missing description for error {:?}", err)
|
write!(f, "todo: missing description for error {:?}", err)
|
||||||
|
@ -15,6 +15,7 @@ use crate::decompressors::{
|
|||||||
ZipDecompressor,
|
ZipDecompressor,
|
||||||
GzipDecompressor,
|
GzipDecompressor,
|
||||||
BzipDecompressor,
|
BzipDecompressor,
|
||||||
|
LzmaDecompressor,
|
||||||
DecompressionResult
|
DecompressionResult
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,12 +32,10 @@ use crate::file::File;
|
|||||||
|
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
pub struct Evaluator {
|
pub struct Evaluator {}
|
||||||
// verbosity: Verbosity
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluator {
|
impl Evaluator {
|
||||||
fn get_compressor(
|
pub fn get_compressor(
|
||||||
file: &File,
|
file: &File,
|
||||||
) -> error::OuchResult<(Option<Box<dyn Compressor>>, Box<dyn Compressor>)> {
|
) -> error::OuchResult<(Option<Box<dyn Compressor>>, Box<dyn Compressor>)> {
|
||||||
if file.extension.is_none() {
|
if file.extension.is_none() {
|
||||||
@ -71,13 +70,12 @@ impl Evaluator {
|
|||||||
CompressionFormat::Tar => Box::new(TarCompressor {}),
|
CompressionFormat::Tar => Box::new(TarCompressor {}),
|
||||||
CompressionFormat::Zip => Box::new(ZipCompressor {}),
|
CompressionFormat::Zip => Box::new(ZipCompressor {}),
|
||||||
_other => todo!()
|
_other => todo!()
|
||||||
//
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((first_compressor, second_compressor))
|
Ok((first_compressor, second_compressor))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_decompressor(
|
pub fn get_decompressor(
|
||||||
file: &File,
|
file: &File,
|
||||||
) -> error::OuchResult<(Option<Box<dyn Decompressor>>, Box<dyn Decompressor>)> {
|
) -> error::OuchResult<(Option<Box<dyn Decompressor>>, Box<dyn Decompressor>)> {
|
||||||
if file.extension.is_none() {
|
if file.extension.is_none() {
|
||||||
@ -95,11 +93,9 @@ impl Evaluator {
|
|||||||
|
|
||||||
CompressionFormat::Zip => Box::new(ZipDecompressor {}),
|
CompressionFormat::Zip => Box::new(ZipDecompressor {}),
|
||||||
|
|
||||||
CompressionFormat::Gzip => Box::new(GzipDecompressor {}),
|
CompressionFormat::Gzip => Box::new(GzipDecompressor{}),
|
||||||
|
|
||||||
CompressionFormat::Lzma => {
|
CompressionFormat::Lzma => Box::new(LzmaDecompressor{}),
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
CompressionFormat::Bzip => {
|
CompressionFormat::Bzip => {
|
||||||
Box::new(BzipDecompressor {})
|
Box::new(BzipDecompressor {})
|
||||||
|
31
src/main.rs
31
src/main.rs
@ -1,7 +1,6 @@
|
|||||||
use std::{convert::TryFrom, io::Write};
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod error;
|
mod error;
|
||||||
@ -34,27 +33,17 @@ fn main() -> error::OuchResult<()>{
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn main() {
|
// fn main() -> error::OuchResult<()> {
|
||||||
// use zip::ZipWriter;
|
// let bytes = fs::read("extension.tar.lzma")?;
|
||||||
|
|
||||||
// let buf = vec![];
|
// let mut ret = vec![];
|
||||||
// let mut writer = zip::ZipWriter::new(std::io::Cursor::new(buf));
|
|
||||||
|
|
||||||
// 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") {
|
// Ok(())
|
||||||
// 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();
|
|
||||||
// }
|
// }
|
31
src/test.rs
31
src/test.rs
@ -201,3 +201,34 @@ mod extension_extraction {
|
|||||||
Ok(())
|
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(())
|
||||||
|
// }
|
||||||
|
// }
|
Loading…
x
Reference in New Issue
Block a user