From 22e131fb460935d5f472b3e3b034cbe7f6d9305c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vin=C3=ADcius=20Rodrigues=20Miguel?= Date: Tue, 23 Mar 2021 21:28:22 -0300 Subject: [PATCH] Add support for zip (and... .zip.zip) compression --- src/cli.rs | 16 ++++---- src/compressors/mod.rs | 7 +++- src/compressors/tar.rs | 15 +++++-- src/compressors/zip.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++ src/decompressors/niffler.rs | 2 +- src/decompressors/tar.rs | 2 +- src/decompressors/zip.rs | 2 +- src/evaluator.rs | 49 +++++++++++++---------- src/file.rs | 4 +- src/main.rs | 30 +++++++++++++- src/test.rs | 10 ++--- 11 files changed, 185 insertions(+), 45 deletions(-) create mode 100644 src/compressors/zip.rs diff --git a/src/cli.rs b/src/cli.rs index 7e9a284..c168667 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,7 @@ use std::{convert::TryFrom, path::PathBuf, vec::Vec}; use clap::{Arg, Values}; -use colored::Colorize; +// use colored::Colorize; use crate::error; use crate::extension::Extension; @@ -97,11 +97,11 @@ impl TryFrom> for Command { if output_is_compressible { // The supplied output is compressible, so we'll compress our inputs to it - println!( - "{}: trying to compress input files into '{}'", - "info".yellow(), - output_file - ); + // println!( + // "{}: trying to compress input files into '{}'", + // "info".yellow(), + // output_file + // ); let input_files = input_files.map(PathBuf::from).collect(); @@ -109,7 +109,7 @@ impl TryFrom> for Command { kind: CommandKind::Compression(input_files), output: Some(File { path: output_file.into(), - contents: None, + contents_in_memory: None, extension: Some(output_file_extension.unwrap()) }), }); @@ -124,7 +124,7 @@ impl TryFrom> for Command { kind: CommandKind::Decompression(input_files), output: Some(File { path: output_file.into(), - contents: None, + contents_in_memory: None, extension: None }) }); diff --git a/src/compressors/mod.rs b/src/compressors/mod.rs index 739a34e..064a954 100644 --- a/src/compressors/mod.rs +++ b/src/compressors/mod.rs @@ -1,6 +1,9 @@ mod tar; +mod zip; mod compressor; -pub use compressor::{Compressor}; +pub use compressor::Compressor; +pub use self::compressor::Entry; pub use self::tar::TarCompressor; -pub use self::compressor::Entry; \ No newline at end of file +pub use self::zip::ZipCompressor; + diff --git a/src/compressors/tar.rs b/src/compressors/tar.rs index dc18c13..8af4f55 100644 --- a/src/compressors/tar.rs +++ b/src/compressors/tar.rs @@ -12,9 +12,10 @@ pub struct TarCompressor {} impl TarCompressor { + // TODO: this function does not seem to be working correctly ;/ fn make_archive_from_memory(input: File) -> OuchResult> { - let contents = match input.contents { + let contents = match input.contents_in_memory { Some(bytes) => bytes, None => { eprintln!("{}: reached TarCompressor::make_archive_from_memory without known content.", "internal error".red()); @@ -24,13 +25,19 @@ impl TarCompressor { let mut header = Header::new_gnu(); - header.set_path(&input.path).unwrap(); + // header.set_path(&input.path.file_stem().unwrap())?; + header.set_path(".")?; header.set_size(contents.len() as u64); header.set_cksum(); + header.set_mode(644); let mut b = Builder::new(Vec::new()); - b.append_data(&mut header, &input.path, &*contents)?; + b.append_data( + &mut header, + &input.path.file_stem().unwrap(), + &*contents + )?; Ok(b.into_inner()?) } @@ -41,6 +48,8 @@ impl TarCompressor { let mut b = Builder::new(buf); for filename in input_filenames { + // TODO: check if filename is a file or a directory + for entry in WalkDir::new(&filename) { let entry = entry?; let path = entry.path(); diff --git a/src/compressors/zip.rs b/src/compressors/zip.rs new file mode 100644 index 0000000..811a197 --- /dev/null +++ b/src/compressors/zip.rs @@ -0,0 +1,93 @@ +use std::{io::{Cursor, Write}, path::PathBuf}; + +use walkdir::WalkDir; + +use crate::{ + compressors::Compressor, + error::{Error, OuchResult}, + file::File, +}; + +use super::compressor::Entry; + +pub struct ZipCompressor {} + +impl ZipCompressor { + // TODO: this function does not seem to be working correctly ;/ + fn make_archive_from_memory(input: File) -> OuchResult> { + let buffer = vec![]; + let mut writer = zip::ZipWriter::new(std::io::Cursor::new(buffer)); + + let inner_file_path: Box = input + .path + .file_stem() + .ok_or( + // TODO: Is this reachable? + Error::InvalidInput + )? + .to_string_lossy() + .into(); + + let options = + zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated); + + // let ok = Box::from(inner_file_path.to_string_lossy()); + writer.start_file(inner_file_path, options)?; + + let input_bytes = match input.contents_in_memory { + Some(bytes) => bytes, + None => { + // TODO: error description, although this block should not be + // reachable + return Err(Error::InvalidInput); + } + }; + + writer.write(&*input_bytes)?; + + + + let bytes = writer.finish().unwrap(); + + Ok(bytes.into_inner()) + } + + fn make_archive_from_files(input_filenames: Vec) -> OuchResult> { + let buffer = vec![]; + let mut writer = zip::ZipWriter::new(Cursor::new(buffer)); + + let options = + zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated); + + for filename in input_filenames { + for entry in WalkDir::new(filename) { + let entry = entry?; + let entry_path = &entry.path(); + if entry_path.is_dir() { + continue; + } + writer + .start_file( + entry_path.to_string_lossy(), + options + )?; + let file_bytes = std::fs::read(entry.path())?; + writer.write(&*file_bytes)?; + } + } + + + let bytes = writer.finish().unwrap(); + + Ok(bytes.into_inner()) + } +} + +impl Compressor for ZipCompressor { + fn compress(&self, from: Entry) -> OuchResult> { + match from { + Entry::Files(filenames) => Ok(Self::make_archive_from_files(filenames)?), + Entry::InMemory(file) => Ok(Self::make_archive_from_memory(file)?), + } + } +} diff --git a/src/decompressors/niffler.rs b/src/decompressors/niffler.rs index 3539c61..3d66583 100644 --- a/src/decompressors/niffler.rs +++ b/src/decompressors/niffler.rs @@ -17,7 +17,7 @@ pub struct NifflerDecompressor {} impl NifflerDecompressor { fn unpack_file(from: &Path) -> OuchResult> { - println!("{}: trying to decompress {:?}", "info".yellow(), from); + // println!("{}: trying to decompress {:?}", "info".yellow(), from); let file = std::fs::read(from)?; diff --git a/src/decompressors/tar.rs b/src/decompressors/tar.rs index 5b23e9d..7e11d6e 100644 --- a/src/decompressors/tar.rs +++ b/src/decompressors/tar.rs @@ -17,7 +17,7 @@ impl TarDecompressor { println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), &from.path); let mut files_unpacked = vec![]; - let mut archive: Archive> = match from.contents { + let mut archive: Archive> = match from.contents_in_memory { Some(bytes) => { tar::Archive::new(Box::new(Cursor::new(bytes))) } diff --git a/src/decompressors/zip.rs b/src/decompressors/zip.rs index 64405ae..4e5bc5e 100644 --- a/src/decompressors/zip.rs +++ b/src/decompressors/zip.rs @@ -80,7 +80,7 @@ impl ZipDecompressor { &from.path ); - match from.contents { + match from.contents_in_memory { Some(bytes) => { let mut archive = zip::ZipArchive::new(Cursor::new(bytes))?; Ok(Self::zip_decompress(&mut archive, into)?) diff --git a/src/evaluator.rs b/src/evaluator.rs index 9b715dc..446bf58 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -2,25 +2,33 @@ use std::{ffi::OsStr, fs, io::Write, path::PathBuf}; use colored::Colorize; -use crate::{compressors::{Entry, TarCompressor}, decompressors::TarDecompressor}; -use crate::decompressors::ZipDecompressor; -use crate::{ - cli::{Command, CommandKind}, - decompressors::{ - Decompressor, - DecompressionResult, - NifflerDecompressor - }, - compressors::Compressor, - error::{self, OuchResult}, - extension::{ - Extension, - CompressionFormat, - }, - file::File, - utils, +use crate::compressors::{ + Entry, + Compressor, + TarCompressor, + ZipCompressor }; +use crate::decompressors::{ + Decompressor, + TarDecompressor, + ZipDecompressor, + NifflerDecompressor, + DecompressionResult +}; + +use crate::extension::{ + Extension, + CompressionFormat +}; + +use crate::cli::{Command, CommandKind}; + +use crate::error::{self, OuchResult}; + +use crate::file::File; + +use crate::utils; pub struct Evaluator { // verbosity: Verbosity @@ -46,7 +54,7 @@ impl Evaluator { Some(ext) => match ext { CompressionFormat::Tar => Some(Box::new(TarCompressor {})), - // CompressionFormat::Zip => Some(Box::new(ZipCompressor {})), + CompressionFormat::Zip => Some(Box::new(ZipCompressor {})), // _other => Some(Box::new(NifflerCompressor {})), _other => { @@ -60,6 +68,7 @@ impl Evaluator { // any let second_compressor: Box = match extension.second_ext { CompressionFormat::Tar => Box::new(TarCompressor {}), + CompressionFormat::Zip => Box::new(ZipCompressor {}), _other => todo!() // }; @@ -138,7 +147,7 @@ impl Evaluator { let file = File { path: filename, - contents: Some(bytes), + contents_in_memory: Some(bytes), extension, }; @@ -166,7 +175,7 @@ impl Evaluator { let mut entry = Entry::Files(files); let bytes = first_compressor.compress(entry)?; - output.contents = Some(bytes); + output.contents_in_memory = Some(bytes); entry = Entry::InMemory(output); diff --git a/src/file.rs b/src/file.rs index 22391ac..fb4e634 100644 --- a/src/file.rs +++ b/src/file.rs @@ -9,7 +9,7 @@ pub struct File { pub path: PathBuf, /// The bytes that compose the file. /// Only used when the whole file is kept in-memory - pub contents: Option>, + pub contents_in_memory: Option>, /// Note: extension here might be a misleading name since /// we don't really care about any extension other than supported compression ones. /// @@ -22,7 +22,7 @@ impl From<(PathBuf, Extension)> for File { fn from((path, format): (PathBuf, Extension)) -> Self { Self { path, - contents: None, + contents_in_memory: None, extension: Some(format), } } diff --git a/src/main.rs b/src/main.rs index 02e4375..02c4d88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ -use std::{convert::TryFrom}; +use std::{convert::TryFrom, io::Write}; use colored::Colorize; +use walkdir::WalkDir; mod cli; mod error; @@ -29,6 +30,31 @@ fn main() -> error::OuchResult<()>{ print_error(err) } } - + Ok(()) } + +// fn main() { +// use zip::ZipWriter; + +// let buf = vec![]; +// let mut writer = zip::ZipWriter::new(std::io::Cursor::new(buf)); + +// let options = zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated); + + +// 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(); +// } \ No newline at end of file diff --git a/src/test.rs b/src/test.rs index e7e1517..1aed192 100644 --- a/src/test.rs +++ b/src/test.rs @@ -23,13 +23,13 @@ mod cli { kind: Decompression(vec![ File { path: "file.zip".into(), - contents: None, + contents_in_memory: None, extension: Some(Extension::from(Zip)) } ]), output: Some(File { path: "folder".into(), - contents: None, + contents_in_memory: None, extension: None }), } @@ -49,12 +49,12 @@ mod cli { kind: Decompression(vec![ File { path: "file.zip".into(), - contents: None, + contents_in_memory: None, extension: Some(Extension::from(Zip)) }, File { path: "file.tar".into(), - contents: None, + contents_in_memory: None, extension: Some(Extension::from(Tar)) } ],), @@ -89,7 +89,7 @@ mod cli { output: Some( File { path: "file.tar".into(), - contents: None, + contents_in_memory: None, extension: Some(Extension::from(Tar)) } ), -- 2.11.4.GIT