mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-05 02:55:31 +00:00
Add support for decompressing .tar.{bz, xz, lz} and .zip.{bz, xz, lz}
This commit is contained in:
parent
77d7613967
commit
e08703850c
@ -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 {
|
||||
|
@ -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>;
|
||||
}
|
@ -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)?;
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -30,5 +30,8 @@ fn main() -> OuchResult<()>{
|
||||
print_error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// let extension = dbg!(Extension::new("file.tar.gz"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
18
src/test.rs
18
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<()> {
|
||||
|
@ -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};
|
||||
|
Loading…
x
Reference in New Issue
Block a user