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 std::{convert::TryFrom, path::PathBuf, vec::Vec};
use clap::{Arg, Values}; use clap::{Arg, Values};
use colored::Colorize; // use colored::Colorize;
use crate::error; use crate::error;
use crate::extension::Extension; use crate::extension::Extension;
@ -97,11 +97,11 @@ impl TryFrom<clap::ArgMatches<'static>> for Command {
if output_is_compressible { if output_is_compressible {
// The supplied output is compressible, so we'll compress our inputs to it // The supplied output is compressible, so we'll compress our inputs to it
println!( // println!(
"{}: trying to compress input files into '{}'", // "{}: trying to compress input files into '{}'",
"info".yellow(), // "info".yellow(),
output_file // output_file
); // );
let input_files = input_files.map(PathBuf::from).collect(); 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), kind: CommandKind::Compression(input_files),
output: Some(File { output: Some(File {
path: output_file.into(), path: output_file.into(),
contents: None, contents_in_memory: None,
extension: Some(output_file_extension.unwrap()) extension: Some(output_file_extension.unwrap())
}), }),
}); });
@ -124,7 +124,7 @@ impl TryFrom<clap::ArgMatches<'static>> for Command {
kind: CommandKind::Decompression(input_files), kind: CommandKind::Decompression(input_files),
output: Some(File { output: Some(File {
path: output_file.into(), path: output_file.into(),
contents: None, contents_in_memory: None,
extension: None extension: None
}) })
}); });

View File

@ -1,6 +1,9 @@
mod tar; mod tar;
mod zip;
mod compressor; mod compressor;
pub use compressor::{Compressor}; pub use compressor::Compressor;
pub use self::compressor::Entry;
pub use self::tar::TarCompressor; 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 { impl TarCompressor {
// TODO: this function does not seem to be working correctly ;/
fn make_archive_from_memory(input: File) -> OuchResult<Vec<u8>> { 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, Some(bytes) => bytes,
None => { None => {
eprintln!("{}: reached TarCompressor::make_archive_from_memory without known content.", "internal error".red()); 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(); 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_size(contents.len() as u64);
header.set_cksum(); header.set_cksum();
header.set_mode(644);
let mut b = Builder::new(Vec::new()); 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()?) Ok(b.into_inner()?)
} }
@ -41,6 +48,8 @@ impl TarCompressor {
let mut b = Builder::new(buf); let mut b = Builder::new(buf);
for filename in input_filenames { for filename in input_filenames {
// TODO: check if filename is a file or a directory
for entry in WalkDir::new(&filename) { for entry in WalkDir::new(&filename) {
let entry = entry?; let entry = entry?;
let path = entry.path(); 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 { impl NifflerDecompressor {
fn unpack_file(from: &Path) -> OuchResult<Vec<u8>> { 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)?; let file = std::fs::read(from)?;

View File

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

View File

@ -80,7 +80,7 @@ impl ZipDecompressor {
&from.path &from.path
); );
match from.contents { match from.contents_in_memory {
Some(bytes) => { Some(bytes) => {
let mut archive = zip::ZipArchive::new(Cursor::new(bytes))?; let mut archive = zip::ZipArchive::new(Cursor::new(bytes))?;
Ok(Self::zip_decompress(&mut archive, into)?) 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 colored::Colorize;
use crate::{compressors::{Entry, TarCompressor}, decompressors::TarDecompressor}; use crate::compressors::{
use crate::decompressors::ZipDecompressor; Entry,
use crate::{ Compressor,
cli::{Command, CommandKind}, TarCompressor,
decompressors::{ ZipCompressor
Decompressor,
DecompressionResult,
NifflerDecompressor
},
compressors::Compressor,
error::{self, OuchResult},
extension::{
Extension,
CompressionFormat,
},
file::File,
utils,
}; };
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 { pub struct Evaluator {
// verbosity: Verbosity // verbosity: Verbosity
@ -46,7 +54,7 @@ impl Evaluator {
Some(ext) => match ext { Some(ext) => match ext {
CompressionFormat::Tar => Some(Box::new(TarCompressor {})), CompressionFormat::Tar => Some(Box::new(TarCompressor {})),
// CompressionFormat::Zip => Some(Box::new(ZipCompressor {})), CompressionFormat::Zip => Some(Box::new(ZipCompressor {})),
// _other => Some(Box::new(NifflerCompressor {})), // _other => Some(Box::new(NifflerCompressor {})),
_other => { _other => {
@ -60,6 +68,7 @@ impl Evaluator {
// any // any
let second_compressor: Box<dyn Compressor> = match extension.second_ext { let second_compressor: Box<dyn Compressor> = match extension.second_ext {
CompressionFormat::Tar => Box::new(TarCompressor {}), CompressionFormat::Tar => Box::new(TarCompressor {}),
CompressionFormat::Zip => Box::new(ZipCompressor {}),
_other => todo!() _other => todo!()
// //
}; };
@ -138,7 +147,7 @@ impl Evaluator {
let file = File { let file = File {
path: filename, path: filename,
contents: Some(bytes), contents_in_memory: Some(bytes),
extension, extension,
}; };
@ -166,7 +175,7 @@ impl Evaluator {
let mut entry = Entry::Files(files); let mut entry = Entry::Files(files);
let bytes = first_compressor.compress(entry)?; let bytes = first_compressor.compress(entry)?;
output.contents = Some(bytes); output.contents_in_memory = Some(bytes);
entry = Entry::InMemory(output); entry = Entry::InMemory(output);

View File

@ -9,7 +9,7 @@ pub struct File {
pub path: PathBuf, pub path: PathBuf,
/// The bytes that compose the file. /// The bytes that compose the file.
/// Only used when the whole file is kept in-memory /// 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 /// Note: extension here might be a misleading name since
/// we don't really care about any extension other than supported compression ones. /// 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 { fn from((path, format): (PathBuf, Extension)) -> Self {
Self { Self {
path, path,
contents: None, contents_in_memory: None,
extension: Some(format), extension: Some(format),
} }
} }

View File

@ -1,6 +1,7 @@
use std::{convert::TryFrom}; use std::{convert::TryFrom, io::Write};
use colored::Colorize; use colored::Colorize;
use walkdir::WalkDir;
mod cli; mod cli;
mod error; mod error;
@ -29,6 +30,31 @@ fn main() -> error::OuchResult<()>{
print_error(err) print_error(err)
} }
} }
Ok(()) 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![ kind: Decompression(vec![
File { File {
path: "file.zip".into(), path: "file.zip".into(),
contents: None, contents_in_memory: None,
extension: Some(Extension::from(Zip)) extension: Some(Extension::from(Zip))
} }
]), ]),
output: Some(File { output: Some(File {
path: "folder".into(), path: "folder".into(),
contents: None, contents_in_memory: None,
extension: None extension: None
}), }),
} }
@ -49,12 +49,12 @@ mod cli {
kind: Decompression(vec![ kind: Decompression(vec![
File { File {
path: "file.zip".into(), path: "file.zip".into(),
contents: None, contents_in_memory: None,
extension: Some(Extension::from(Zip)) extension: Some(Extension::from(Zip))
}, },
File { File {
path: "file.tar".into(), path: "file.tar".into(),
contents: None, contents_in_memory: None,
extension: Some(Extension::from(Tar)) extension: Some(Extension::from(Tar))
} }
],), ],),
@ -89,7 +89,7 @@ mod cli {
output: Some( output: Some(
File { File {
path: "file.tar".into(), path: "file.tar".into(),
contents: None, contents_in_memory: None,
extension: Some(Extension::from(Tar)) extension: Some(Extension::from(Tar))
} }
), ),