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 crate::error;
use crate::extension::{Extension, CompressionFormat};
use crate::extension::Extension;
use crate::file::File;
#[derive(PartialEq, Eq, Debug)]

View File

@ -2,22 +2,17 @@ use std::path::PathBuf;
use crate::{error::OuchResult, file::File};
pub enum CompressionResult {
ZipArchive(Vec<u8>),
TarArchive(Vec<u8>),
FileInMemory(Vec<u8>)
// pub enum CompressionResult {
// ZipArchive(Vec<u8>),
// TarArchive(Vec<u8>),
// FileInMemory(Vec<u8>)
// }
pub enum Entry {
Files(Vec<PathBuf>),
InMemory(File)
}
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 compressor;
pub use compressor::{Compressor, CompressionResult};
pub use compressor::{Compressor};
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 crate::{decompressors::TarDecompressor, error::OuchResult};
use crate::compressors::Compressor;
use super::compressor::CompressionResult;
use crate::{compressors::Compressor, error::{Error, OuchResult}, file::{self, File}};
pub struct 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 mut b = Builder::new(buf);
for file in input_files {
for entry in WalkDir::new(&file.path) {
for entry in WalkDir::new(&file) {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
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()?)
}
}
impl Compressor for TarCompressor {
fn compress(&self, from: Vec<crate::file::File>) -> OuchResult<CompressionResult> {
Ok(CompressionResult::TarArchive(
TarCompressor::make_archive_in_memory(from)?
))
fn compress(&self, from: Vec<PathBuf>) -> OuchResult<Vec<u8>> {
Ok(
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 crate::{decompressors::Decompressor, extension::{self, Extension}};
use crate::decompressors::TarDecompressor;
use crate::{compressors::TarCompressor, decompressors::TarDecompressor};
use crate::decompressors::ZipDecompressor;
use crate::{
cli::{Command, CommandKind},
decompressors::{DecompressionResult, NifflerDecompressor},
decompressors::{
Decompressor,
DecompressionResult,
NifflerDecompressor
},
compressors::Compressor,
error::{self, OuchResult},
extension::CompressionFormat,
extension::{
Extension,
CompressionFormat,
},
file::File,
utils,
};
pub struct Evaluator {
// verbosity: Verbosity
}
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(
file: &File,
) -> error::OuchResult<(Option<Box<dyn Decompressor>>, Box<dyn Decompressor>)> {
@ -31,7 +79,7 @@ impl Evaluator {
return Err(error::Error::InvalidInput);
}
let extension = file.extension.clone().unwrap();
let second_decompressor: Box<dyn Decompressor> = match extension.second_ext {
CompressionFormat::Tar => Box::new(TarDecompressor {}),
@ -64,10 +112,11 @@ impl Evaluator {
output_file: &Option<File>,
extension: Option<Extension>,
) -> OuchResult<()> {
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 == "." {
// 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.
@ -97,7 +146,6 @@ impl Evaluator {
// 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)?;
if let DecompressionResult::FileInMemory(_) = decompression_result {
// Should not be reachable.
@ -107,6 +155,11 @@ impl Evaluator {
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<()> {
// let output_file = &command.output;
let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?;
@ -120,7 +173,13 @@ impl Evaluator {
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_path, first_decompressor, output, extension)?;
Self::decompress_file_in_memory(
bytes,
file_path,
first_decompressor,
output,
extension,
)?;
}
DecompressionResult::FilesUnpacked(_files) => {
// If the file's last extension was an archival method,
@ -138,12 +197,12 @@ impl Evaluator {
pub fn evaluate(command: Command) -> error::OuchResult<()> {
let output = command.output.clone();
match command.kind {
CommandKind::Compression(files_to_compress) => {
for _file in files_to_compress {
todo!();
}
// Safe to unwrap since output is mandatory for compression
let output = output.unwrap();
Self::compress_files(files_to_compress, output)?;
}
CommandKind::Decompression(files_to_decompress) => {
for file in files_to_decompress {

View File

@ -1,6 +1,6 @@
use std::path::PathBuf;
use crate::extension::{CompressionFormat, Extension};
use crate::extension::Extension;
#[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;
@ -13,43 +13,39 @@ mod utils;
mod compressors;
mod decompressors;
use compressors::{CompressionResult, Compressor, TarCompressor};
use error::OuchResult;
use file::File;
fn main() -> error::OuchResult<()>{
let print_error = |err| {
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 print_error = |err| {
// println!("{}: {}", "error".red(), err);
// let compressor = TarCompressor {};
// 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 {
path: PathBuf::from("target"),
contents: None,
extension: None,
};
// let ok = match ok {
// CompressionResult::TarArchive(bytes) => bytes,
// _ => unreachable!()
// };
let ok = compressor.compress(vec![file])?;
let ok = match ok {
CompressionResult::TarArchive(bytes) => bytes,
_ => unreachable!()
};
fs::write(Path::new("great.tar"), ok)?;
// fs::write(Path::new("great.tar"), ok)?;
Ok(())
}