Add support for zip (and... .zip.zip) compression

This commit is contained in:
Vinícius Rodrigues Miguel 2021-03-23 21:28:22 -03:00
parent d72ca9eeae
commit 22e131fb46
11 changed files with 185 additions and 45 deletions

View File

@ -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<clap::ArgMatches<'static>> 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<clap::ArgMatches<'static>> 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<clap::ArgMatches<'static>> for Command {
kind: CommandKind::Decompression(input_files),
output: Some(File {
path: output_file.into(),
contents: None,
contents_in_memory: None,
extension: None
})
});

View File

@ -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;
pub use self::zip::ZipCompressor;

View File

@ -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<Vec<u8>> {
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();

93
src/compressors/zip.rs Normal file
View File

@ -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<Vec<u8>> {
let buffer = vec![];
let mut writer = zip::ZipWriter::new(std::io::Cursor::new(buffer));
let inner_file_path: Box<str> = 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<PathBuf>) -> OuchResult<Vec<u8>> {
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<Vec<u8>> {
match from {
Entry::Files(filenames) => Ok(Self::make_archive_from_files(filenames)?),
Entry::InMemory(file) => Ok(Self::make_archive_from_memory(file)?),
}
}
}

View File

@ -17,7 +17,7 @@ pub struct NifflerDecompressor {}
impl NifflerDecompressor {
fn unpack_file(from: &Path) -> OuchResult<Vec<u8>> {
println!("{}: trying to decompress {:?}", "info".yellow(), from);
// println!("{}: trying to decompress {:?}", "info".yellow(), from);
let file = std::fs::read(from)?;

View File

@ -17,7 +17,7 @@ impl TarDecompressor {
println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), &from.path);
let mut files_unpacked = vec![];
let mut archive: Archive<Box<dyn Read>> = match from.contents {
let mut archive: Archive<Box<dyn Read>> = match from.contents_in_memory {
Some(bytes) => {
tar::Archive::new(Box::new(Cursor::new(bytes)))
}

View File

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

View File

@ -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<dyn Compressor> = 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);

View File

@ -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<Vec<u8>>,
pub contents_in_memory: Option<Vec<u8>>,
/// 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),
}
}

View File

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

View File

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