mirror of
https://github.com/ouch-org/ouch.git
synced 2025-07-22 17:40:17 +00:00
Add support for zip (and... .zip.zip) compression
This commit is contained in:
parent
d72ca9eeae
commit
22e131fb46
16
src/cli.rs
16
src/cli.rs
@ -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
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
93
src/compressors/zip.rs
Normal 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)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)?;
|
||||||
|
|
||||||
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
@ -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)?)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/main.rs
30
src/main.rs
@ -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();
|
||||||
|
// }
|
10
src/test.rs
10
src/test.rs
@ -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))
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user