Skip to content

Commit

Permalink
Merge pull request #28 from tamada/lha+zstd
Browse files Browse the repository at this point in the history
add features to extract lha/lzh and zstd
  • Loading branch information
tamada authored May 26, 2024
2 parents 526f7a6 + b510c80 commit 36a8a7e
Show file tree
Hide file tree
Showing 11 changed files with 353 additions and 61 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "totebag"
version = "0.4.6"
description = "A tool for archiving files and directories and extracting several archive formats."
description = "A tool for extracting/archiving files and directories in multiple formats."
repository = "https://github.com/tamada/totebag"
readme = "README.md"
authors = [
Expand All @@ -16,13 +16,15 @@ edition = "2021"
[dependencies]
bzip2 = "0.4.4"
clap = { version = "4.5.4", features = ["derive"] }
delharc = "0.6.1"
flate2 = "1.0.29"
sevenz-rust = "0.6.0"
tar = "0.4.40"
time = "0.3.36"
unrar = "0.5.3"
xz2 = "0.1.7"
zip = "1.1.1"
zip = "2.1.0"
zstd = "0.13.1"

[build-dependencies]
clap = { version = "4.5.4", features = ["derive"] }
Expand Down
20 changes: 16 additions & 4 deletions src/archiver.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use std::fs::{create_dir_all, File};
use std::path::PathBuf;

use crate::archiver::lha::LhaArchiver;
use crate::archiver::rar::RarArchiver;
use crate::archiver::sevenz::SevenZArchiver;
use crate::archiver::tar::{TarArchiver, TarBz2Archiver, TarGzArchiver, TarXzArchiver};
use crate::archiver::tar::{TarArchiver, TarBz2Archiver, TarGzArchiver, TarXzArchiver, TarZstdArchiver};
use crate::archiver::zip::ZipArchiver;
use crate::cli::{Result, ToteError};
use crate::format::{find_format, Format};
use crate::verboser::{create_verboser, Verboser};
use crate::CliOpts;

mod lha;
mod os;
mod rar;
mod sevenz;
Expand All @@ -31,6 +33,8 @@ pub fn create_archiver(dest: &PathBuf) -> Result<Box<dyn Archiver>> {
Format::TarGz => Ok(Box::new(TarGzArchiver {})),
Format::TarBz2 => Ok(Box::new(TarBz2Archiver {})),
Format::TarXz => Ok(Box::new(TarXzArchiver {})),
Format::TarZstd => Ok(Box::new(TarZstdArchiver {})),
Format::LHA => Ok(Box::new(LhaArchiver {})),
Format::Rar => Ok(Box::new(RarArchiver {})),
Format::SevenZ => Ok(Box::new(SevenZArchiver {})),
_ => Err(ToteError::UnknownFormat(format.to_string())),
Expand Down Expand Up @@ -160,9 +164,17 @@ mod tests {
assert!(a7.is_ok());
assert_eq!(a7.unwrap().format(), Format::SevenZ);

let a8 = create_archiver(&PathBuf::from("results/test.unknown"));
assert!(a8.is_err());
if let Err(e) = a8 {
let a8 = create_archiver(&PathBuf::from("results/test.tar.zst"));
assert!(a8.is_ok());
assert_eq!(a8.unwrap().format(), Format::TarZstd);

let a9 = create_archiver(&PathBuf::from("results/test.lha"));
assert!(a9.is_ok());
assert_eq!(a9.unwrap().format(), Format::LHA);

let a10 = create_archiver(&PathBuf::from("results/test.unknown"));
assert!(a10.is_err());
if let Err(e) = a10 {
if let ToteError::UnknownFormat(msg) = e {
assert_eq!(msg, "test.unknown: unknown format".to_string());
} else {
Expand Down
42 changes: 42 additions & 0 deletions src/archiver/lha.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::archiver::{Archiver, Format, ArchiverOpts};
use crate::cli::{ToteError, Result};

pub(super) struct LhaArchiver {
}

impl Archiver for LhaArchiver {
fn perform(&self, _: &ArchiverOpts) -> Result<()> {
Err(ToteError::UnsupportedFormat("only extraction support for lha".to_string()))
}
fn format(&self) -> Format {
Format::LHA
}
}

#[cfg(test)]
mod tests {
use super::*;

use std::path::PathBuf;
use crate::verboser::create_verboser;

#[test]
fn test_format() {
let archiver = LhaArchiver{};
assert_eq!(archiver.format(), Format::LHA);
}

#[test]
fn test_archive() {
let archiver = LhaArchiver{};
let opts = ArchiverOpts {
dest: PathBuf::from("results/test.rar"),
targets: vec![],
overwrite: false,
recursive: false,
v: create_verboser(false),
};
let r = archiver.perform(&opts);
assert!(r.is_err());
}
}
48 changes: 45 additions & 3 deletions src/archiver/tar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ pub(super) struct TarGzArchiver {
}
pub(super) struct TarBz2Archiver {
}

pub(super) struct TarXzArchiver {
}
pub(super) struct TarZstdArchiver {
}

impl Archiver for TarArchiver {
fn perform(&self, inout: &ArchiverOpts) -> Result<()> {
Expand All @@ -36,6 +37,7 @@ impl Archiver for TarGzArchiver{
Format::TarGz
}
}

impl Archiver for TarBz2Archiver {
fn perform(&self, opts: &ArchiverOpts) -> Result<()> {
write_tar(opts, |file| BzEncoder::new(file, bzip2::Compression::best()))
Expand All @@ -49,11 +51,23 @@ impl Archiver for TarXzArchiver {
fn perform(&self, inout: &ArchiverOpts) -> Result<()> {
write_tar(inout, |file| XzEncoder::new(file, 9))
}

fn format(&self) -> Format {
Format::TarXz
}
}

impl Archiver for TarZstdArchiver {
fn perform(&self, inout: &ArchiverOpts) -> Result<()> {
write_tar(inout, |file|
zstd::Encoder::new(file, 9).unwrap())
}

fn format(&self) -> Format {
Format::TarZstd
}
}

fn write_tar<F, W: Write>(opts: &ArchiverOpts, f: F) -> Result<()>
where F: FnOnce(File) -> W {
match opts.destination() {
Expand Down Expand Up @@ -110,16 +124,16 @@ fn process_file<W: Write>(builder: &mut Builder<W>, target: PathBuf) -> Result<(
mod tests {
use std::path::PathBuf;

use super::*;
use crate::archiver::Archiver;
use crate::archiver::tar::{TarArchiver, TarGzArchiver, TarBz2Archiver};
use crate::archiver::ArchiverOpts;
use crate::format::Format;

fn run_test<F>(f: F)
where
F: FnOnce() -> PathBuf,
{
// setup(); // 予めやりたい処理
// setup(); // preprocessing process
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
match result {
Ok(path) => teardown(path),
Expand Down Expand Up @@ -170,6 +184,34 @@ mod tests {
});
}

#[test]
fn test_tarxz() {
run_test(|| {
let archiver = TarXzArchiver{};
let inout = ArchiverOpts::create(PathBuf::from("results/test.tar.xz"), vec![PathBuf::from("src"), PathBuf::from("Cargo.toml")], true, true, false);
let result = archiver.perform(&inout);
let path = PathBuf::from("results/test.tar.xz");
assert!(result.is_ok());
assert!(path.exists());
assert_eq!(archiver.format(), Format::TarXz);
path
});
}

#[test]
fn test_tarzstd() {
run_test(|| {
let archiver = TarZstdArchiver{};
let inout = ArchiverOpts::create(PathBuf::from("results/test.tar.zst"), vec![PathBuf::from("src"), PathBuf::from("Cargo.toml")], true, true, false);
let result = archiver.perform(&inout);
let path = PathBuf::from("results/test.tar.zst");
assert!(result.is_ok());
assert!(path.exists());
assert_eq!(archiver.format(), Format::TarZstd);
path
});
}

fn teardown(path: PathBuf) {
let _ = std::fs::remove_file(path);
}
Expand Down
50 changes: 30 additions & 20 deletions src/extractor.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::path::PathBuf;

use crate::format::{find_format, Format};
use crate::cli::{Result, ToteError};
use crate::CliOpts;
use crate::format::{find_format, Format};
use crate::verboser::{create_verboser, Verboser};
use crate::CliOpts;

mod zip;
mod lha;
mod rar;
mod tar;
mod sevenz;
mod tar;
mod zip;

pub struct ExtractorOpts {
pub dest: PathBuf,
Expand All @@ -21,23 +22,23 @@ impl ExtractorOpts {
pub fn new(opts: &CliOpts) -> ExtractorOpts {
let d = opts.output.clone();
ExtractorOpts {
dest: d.unwrap_or_else(|| {
PathBuf::from(".")
}),
dest: d.unwrap_or_else(|| PathBuf::from(".")),
use_archive_name_dir: opts.to_archive_name_dir,
overwrite: opts.overwrite,
v: create_verboser(opts.verbose),
}
}

/// Returns the base of the destination directory for the archive file.
/// The target is the archive file name of source.
pub fn destination(&self, target: &PathBuf) -> PathBuf {
if self.use_archive_name_dir {
let file_name = target.file_name().unwrap().to_str().unwrap();
let ext = target.extension().unwrap().to_str().unwrap();
let dir_name = file_name.trim_end_matches(ext)
.trim_end_matches(".").to_string();
let dir_name = file_name
.trim_end_matches(ext)
.trim_end_matches(".")
.to_string();
self.dest.join(dir_name)
} else {
self.dest.clone()
Expand All @@ -56,21 +57,30 @@ pub fn create_extractor(file: &PathBuf) -> Result<Box<dyn Extractor>> {
match format {
Ok(format) => {
return match format {
Format::Zip => Ok(Box::new(zip::ZipExtractor{})),
Format::Rar => Ok(Box::new(rar::RarExtractor{})),
Format::Tar => Ok(Box::new(tar::TarExtractor{})),
Format::TarGz => Ok(Box::new(tar::TarGzExtractor{})),
Format::TarBz2 => Ok(Box::new(tar::TarBz2Extractor{})),
Format::TarXz => Ok(Box::new(tar::TarXzExtractor{})),
Format::SevenZ => Ok(Box::new(sevenz::SevenZExtractor{})),
Format::Unknown(s) => Err(ToteError::UnknownFormat(format!("{}: unsupported format", s))),
Format::Zip => Ok(Box::new(zip::ZipExtractor {})),
Format::Rar => Ok(Box::new(rar::RarExtractor {})),
Format::Tar => Ok(Box::new(tar::TarExtractor {})),
Format::TarGz => Ok(Box::new(tar::TarGzExtractor {})),
Format::TarBz2 => Ok(Box::new(tar::TarBz2Extractor {})),
Format::TarXz => Ok(Box::new(tar::TarXzExtractor {})),
Format::TarZstd => Ok(Box::new(tar::TarZstdExtractor {})),
Format::LHA => Ok(Box::new(lha::LhaExtractor {})),
Format::SevenZ => Ok(Box::new(sevenz::SevenZExtractor {})),
Format::Unknown(s) => Err(ToteError::UnknownFormat(format!(
"{}: unsupported format",
s
))),
}
}
Err(msg) => Err(msg),
}
}

pub fn extractor_info(extractor: &Box<dyn Extractor>, target: &PathBuf, opts: &ExtractorOpts) -> String {
pub fn extractor_info(
extractor: &Box<dyn Extractor>,
target: &PathBuf,
opts: &ExtractorOpts,
) -> String {
format!(
"Format: {:?}\nFile: {:?}\nDestination: {:?}",
extractor.format(),
Expand Down Expand Up @@ -142,4 +152,4 @@ mod tests {
assert!(false);
}
}
}
}
Loading

0 comments on commit 36a8a7e

Please sign in to comment.