mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-05 02:55:31 +00:00
Add Tar compression for in-memory buffers
This commit is contained in:
parent
fa2fb675de
commit
2c0f2b380c
@ -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)]
|
||||
|
@ -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>>;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
mod tar;
|
||||
mod compressor;
|
||||
|
||||
pub use compressor::{Compressor, CompressionResult};
|
||||
pub use compressor::{Compressor};
|
||||
pub use self::tar::TarCompressor;
|
@ -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>;
|
||||
}
|
@ -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 {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::extension::{CompressionFormat, Extension};
|
||||
use crate::extension::Extension;
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
62
src/main.rs
62
src/main.rs
@ -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(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user