Add Tar compression for in-memory buffers

This commit is contained in:
Vinícius Rodrigues Miguel 2021-03-23 02:15:06 -03:00
parent fa2fb675de
commit 2c0f2b380c
7 changed files with 151 additions and 81 deletions

View File

@ -4,7 +4,7 @@ use clap::{Arg, Values};
use colored::Colorize; use colored::Colorize;
use crate::error; use crate::error;
use crate::extension::{Extension, CompressionFormat}; use crate::extension::Extension;
use crate::file::File; use crate::file::File;
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]

View File

@ -2,22 +2,17 @@ use std::path::PathBuf;
use crate::{error::OuchResult, file::File}; use crate::{error::OuchResult, file::File};
pub enum CompressionResult { // pub enum CompressionResult {
ZipArchive(Vec<u8>), // ZipArchive(Vec<u8>),
TarArchive(Vec<u8>), // TarArchive(Vec<u8>),
FileInMemory(Vec<u8>) // FileInMemory(Vec<u8>)
// }
pub enum Entry {
Files(Vec<PathBuf>),
InMemory(File)
} }
pub trait Compressor { pub trait Compressor {
fn compress(&self, from: Vec<File>) -> OuchResult<CompressionResult>; fn compress(&self, from: Vec<PathBuf>) -> OuchResult<Vec<u8>>;
} }
//
//
//
//
//
//
//
//
//

View File

@ -1,5 +1,5 @@
mod tar; mod tar;
mod compressor; mod compressor;
pub use compressor::{Compressor, CompressionResult}; pub use compressor::{Compressor};
pub use self::tar::TarCompressor; pub use self::tar::TarCompressor;

View File

@ -1,42 +1,62 @@
use std::{fs::File, path::Path}; use std::{fs, path::PathBuf};
use tar::Builder; use colored::Colorize;
use tar::{Builder, Header};
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::{decompressors::TarDecompressor, error::OuchResult}; use crate::{compressors::Compressor, error::{Error, OuchResult}, file::{self, File}};
use crate::compressors::Compressor;
use super::compressor::CompressionResult;
pub struct TarCompressor {} pub struct TarCompressor {}
impl TarCompressor { impl TarCompressor {
fn make_archive_in_memory(input_files: Vec<crate::file::File>) -> OuchResult<Vec<u8>> {
fn make_archive_from_memory(input: File) -> OuchResult<Vec<u8>> {
let contents = match input.contents {
Some(bytes) => bytes,
None => {
eprintln!("{}: reached TarCompressor::make_archive_from_memory without known content.", "internal error".red());
return Err(Error::InvalidInput);
}
};
let mut header = Header::new_gnu();
header.set_path(&input.path).unwrap();
header.set_size(contents.len() as u64);
header.set_cksum();
let mut b = Builder::new(Vec::new());
b.append_data(&mut header, &input.path, &*contents)?;
Ok(b.into_inner()?)
}
fn make_archive_from_files(input_files: Vec<PathBuf>) -> OuchResult<Vec<u8>> {
let buf = Vec::new(); let buf = Vec::new();
let mut b = Builder::new(buf); let mut b = Builder::new(buf);
for file in input_files { for file in input_files {
for entry in WalkDir::new(&file.path) { for entry in WalkDir::new(&file) {
let entry = entry.unwrap(); let entry = entry.unwrap();
let path = entry.path(); let path = entry.path();
if path.is_dir() { if path.is_dir() {
continue; continue;
} }
b.append_file(path, &mut File::open(path).unwrap()).unwrap(); b.append_file(path, &mut fs::File::open(path).unwrap()).unwrap();
} }
} }
Ok(b.into_inner()?) Ok(b.into_inner()?)
} }
} }
impl Compressor for TarCompressor { impl Compressor for TarCompressor {
fn compress(&self, from: Vec<crate::file::File>) -> OuchResult<CompressionResult> { fn compress(&self, from: Vec<PathBuf>) -> OuchResult<Vec<u8>> {
Ok(CompressionResult::TarArchive( Ok(
TarCompressor::make_archive_in_memory(from)? TarCompressor::make_archive_from_files(from)?
)) )
} }
} }
// fn compress(&self, from: Vec<File>, into: &Option<File>) -> OuchResult<CompressionResult>;

View File

@ -2,23 +2,71 @@ use std::{ffi::OsStr, fs, io::Write, path::PathBuf};
use colored::Colorize; use colored::Colorize;
use crate::{decompressors::Decompressor, extension::{self, Extension}}; use crate::{compressors::TarCompressor, decompressors::TarDecompressor};
use crate::decompressors::TarDecompressor;
use crate::decompressors::ZipDecompressor; use crate::decompressors::ZipDecompressor;
use crate::{ use crate::{
cli::{Command, CommandKind}, cli::{Command, CommandKind},
decompressors::{DecompressionResult, NifflerDecompressor}, decompressors::{
Decompressor,
DecompressionResult,
NifflerDecompressor
},
compressors::Compressor,
error::{self, OuchResult}, error::{self, OuchResult},
extension::CompressionFormat, extension::{
Extension,
CompressionFormat,
},
file::File, file::File,
utils, utils,
}; };
pub struct Evaluator { pub struct Evaluator {
// verbosity: Verbosity // verbosity: Verbosity
} }
impl Evaluator { impl Evaluator {
fn get_compressor(
file: &File,
) -> error::OuchResult<(Option<Box<dyn Compressor>>, Box<dyn Compressor>)> {
if file.extension.is_none() {
// This block *should* be unreachable
eprintln!(
"{}: reached Evaluator::get_decompressor without known extension.",
"internal error".red()
);
return Err(error::Error::InvalidInput);
}
let extension = file.extension.clone().unwrap();
// Supported first compressors:
// .tar and .zip
let first_compressor: Option<Box<dyn Compressor>> = match extension.first_ext {
Some(ext) => match ext {
CompressionFormat::Tar => Some(Box::new(TarCompressor {})),
// CompressionFormat::Zip => Some(Box::new(ZipCompressor {})),
// _other => Some(Box::new(NifflerCompressor {})),
_other => {
todo!();
}
},
None => None,
};
// Supported second compressors:
// any
let second_compressor: Box<dyn Compressor> = match extension.second_ext {
CompressionFormat::Tar => Box::new(TarCompressor {}),
_other => todo!()
//
};
Ok((first_compressor, second_compressor))
}
fn get_decompressor( 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>)> {
@ -31,7 +79,7 @@ impl Evaluator {
return Err(error::Error::InvalidInput); return Err(error::Error::InvalidInput);
} }
let extension = file.extension.clone().unwrap(); let extension = file.extension.clone().unwrap();
let second_decompressor: Box<dyn Decompressor> = match extension.second_ext { let second_decompressor: Box<dyn Decompressor> = match extension.second_ext {
CompressionFormat::Tar => Box::new(TarDecompressor {}), CompressionFormat::Tar => Box::new(TarDecompressor {}),
@ -64,10 +112,11 @@ impl Evaluator {
output_file: &Option<File>, output_file: &Option<File>,
extension: Option<Extension>, extension: Option<Extension>,
) -> OuchResult<()> { ) -> OuchResult<()> {
let output_file_path = 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_path.as_os_str()); let mut filename = file_path
.file_stem()
.unwrap_or(output_file_path.as_os_str());
if filename == "." { if filename == "." {
// I believe this is only possible when the supplied inout has a name // 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. // of the sort `.tar` or `.zip' and no output has been supplied.
@ -97,7 +146,6 @@ impl Evaluator {
// If there is a decompressor to use, we'll create a file in-memory and decompress it // 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)?; let decompression_result = decompressor.decompress(file, output_file)?;
if let DecompressionResult::FileInMemory(_) = decompression_result { if let DecompressionResult::FileInMemory(_) = decompression_result {
// Should not be reachable. // Should not be reachable.
@ -107,6 +155,11 @@ impl Evaluator {
Ok(()) Ok(())
} }
fn compress_files(files: Vec<PathBuf>, output: File) -> error::OuchResult<()> {
let (first_decompressor, second_decompressor) = Self::get_compressor(&output)?;
Ok(())
}
fn decompress_file(file: File, output: &Option<File>) -> error::OuchResult<()> { fn decompress_file(file: File, output: &Option<File>) -> error::OuchResult<()> {
// let output_file = &command.output; // let output_file = &command.output;
let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?; let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?;
@ -120,7 +173,13 @@ impl Evaluator {
DecompressionResult::FileInMemory(bytes) => { DecompressionResult::FileInMemory(bytes) => {
// We'll now decompress a file currently in memory. // We'll now decompress a file currently in memory.
// This will currently happen in the case of .bz, .xz and .lzma // This will currently happen in the case of .bz, .xz and .lzma
Self::decompress_file_in_memory(bytes, file_path, first_decompressor, output, extension)?; 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, // If the file's last extension was an archival method,
@ -138,12 +197,12 @@ impl Evaluator {
pub fn evaluate(command: Command) -> error::OuchResult<()> { pub fn evaluate(command: Command) -> error::OuchResult<()> {
let output = command.output.clone(); let output = command.output.clone();
match command.kind { match command.kind {
CommandKind::Compression(files_to_compress) => { CommandKind::Compression(files_to_compress) => {
for _file in files_to_compress { // Safe to unwrap since output is mandatory for compression
todo!(); let output = output.unwrap();
} Self::compress_files(files_to_compress, output)?;
} }
CommandKind::Decompression(files_to_decompress) => { CommandKind::Decompression(files_to_decompress) => {
for file in files_to_decompress { for file in files_to_decompress {

View File

@ -1,6 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use crate::extension::{CompressionFormat, Extension}; use crate::extension::Extension;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View File

@ -1,4 +1,4 @@
use std::{convert::TryFrom, fs, path::{Path, PathBuf}}; use std::{convert::TryFrom};
use colored::Colorize; use colored::Colorize;
@ -13,43 +13,39 @@ mod utils;
mod compressors; mod compressors;
mod decompressors; mod decompressors;
use compressors::{CompressionResult, Compressor, TarCompressor}; fn main() -> error::OuchResult<()>{
use error::OuchResult; let print_error = |err| {
use file::File; println!("{}: {}", "error".red(), err);
};
let matches = cli::get_matches();
match cli::Command::try_from(matches) {
Ok(command) => {
match evaluator::Evaluator::evaluate(command) {
Ok(_) => {},
Err(err) => print_error(err)
}
}
Err(err) => {
print_error(err)
}
}
fn main() -> OuchResult<()>{ // let compressor = TarCompressor {};
// let print_error = |err| {
// println!("{}: {}", "error".red(), err); // let file = File {
// path: PathBuf::from("target"),
// contents: None,
// extension: None,
// }; // };
// let matches = cli::get_matches();
// match cli::Command::try_from(matches) {
// Ok(command) => {
// match evaluator::Evaluator::evaluate(command) {
// Ok(_) => {},
// Err(err) => print_error(err)
// }
// }
// Err(err) => {
// print_error(err)
// }
// }
let compressor = TarCompressor {}; // let ok = compressor.compress(vec![file])?;
let file = File { // let ok = match ok {
path: PathBuf::from("target"), // CompressionResult::TarArchive(bytes) => bytes,
contents: None, // _ => unreachable!()
extension: None, // };
};
let ok = compressor.compress(vec![file])?; // fs::write(Path::new("great.tar"), ok)?;
let ok = match ok {
CompressionResult::TarArchive(bytes) => bytes,
_ => unreachable!()
};
fs::write(Path::new("great.tar"), ok)?;
Ok(()) Ok(())
} }