Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/v0.3.0 #19

Merged
merged 9 commits into from
May 14, 2024
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "totebag"
version = "0.2.1"
version = "0.3.0"
description = "A tool for archiving files and directories and extracting several archive formats."
repository = "https://github.com/tamada/totebag"
readme = "README.md"
Expand All @@ -17,12 +17,15 @@ edition = "2021"
bzip2 = "0.4.4"
clap = { version = "4.5.4", features = ["derive"] }
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"

[build-dependencies]
clap = { version = "4.5.4", features = ["derive"] }
clap_complete = "4.5.2"
toml = "0.8.12"

45 changes: 27 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# totebag

[![Version](https://shields.io/badge/Version-0.2.1-blue)](https://github.com/tamada/totebag/releases/tag/v0.2.1)
[![Version](https://shields.io/badge/Version-0.3.0-blue)](https://github.com/tamada/totebag/releases/tag/v0.3.0)
[![MIT License](https://shields.io/badge/License-MIT-blue)](https://github.com/tamada/totebag/blob/main/LICENSE)

[![build](https://github.com/tamada/totebag/actions/workflows/build.yaml/badge.svg)](https://github.com/tamada/totebag/actions/workflows/build.yaml)
Expand All @@ -18,25 +18,34 @@ The tool can extract archive files and archive files and directories.
## Usage

```sh
totebag [OPTIONS] <ARGUMENTS...>
OPTIONS
-m, --mode <MODE> Mode of operation. available: extract, archive, and auto.
Default is auto.
-d, --dest <DEST> Destination of the extraction results.
Default is the current directory.
-o, --output <FILE> Output file for the archive.
Default is the totebag.zip.
The archive formats are guessed form extension of the file name.
--overwrite Overwrite the output file if it exists.
-v, --verbose Display verbose output.
-h, --help Display this help message.
ARGUMENTS
extract mode: archive files to be extracted.
archive mode: files to be archived.
auto mode: if the arguments have archive files, it will extract them.
Otherwise, it will archive the files.
A tool for archiving files and directories and extracting several archive formats.

Usage: totebag [OPTIONS] [ARGUMENTS]...

Arguments:
[ARGUMENTS]... List of files or directories to be processed.

Options:
-m, --mode <MODE> Mode of operation. [default: auto] [possible values: auto, archive, extract, list]
-o, --output <DEST> Output file in archive mode, or output directory in extraction mode
--to-archive-name-dir extract files to DEST/ARCHIVE_NAME directory (extract mode).
-n, --no-recursive No recursive directory (archive mode).
-v, --verbose Display verbose output.
--overwrite Overwrite existing files.
-h, --help Print help
-V, --version Print version
```

Supported archive formats:

- Tar
- Tar+Gzip
- Tar+Bzip2
- Tar+Xz
- Zip
- 7z
- Rar (extraction only)

## Install

```sh
Expand Down
32 changes: 22 additions & 10 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,36 @@ use std::path::Path;

include!("src/cli.rs");

fn generate(s: Shell, app: &mut Command, outdir: &Path, file: &str) {
fn generate(s: Shell, app: &mut Command, appname: &str, outdir: &Path, file: String) {
let destfile = outdir.join(file);
println!("dest: {}", destfile.display());
std::fs::create_dir_all(destfile.parent().unwrap()).unwrap();
let mut dest = File::create(destfile).unwrap();

clap_complete::generate(s, app, appname, &mut dest);
}

fn parse_cargo_toml() -> toml::Value {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
let file = match std::fs::read_to_string(path) {
Ok(f) => f,
Err(e) => panic!("{}", e),
};

clap_complete::generate(s, app, "totebag", &mut dest);
file.parse().unwrap()
}

fn main() {
let table = parse_cargo_toml();
let appname = table["package"]["name"].as_str().unwrap();

let mut app = CliOpts::command();
app.set_bin_name("totebag");
app.set_bin_name(appname);

let outdir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("target/completions/");

generate(Shell::Bash, &mut app, &outdir, "bash/totebag");
generate(Shell::Elvish, &mut app, &outdir, "elvish/totebag");
generate(Shell::Fish, &mut app, &outdir, "fish/totebag");
generate(Shell::PowerShell, &mut app, &outdir, "powershell/totebag");
generate(Shell::Zsh, &mut app, &outdir, "zsh/_totebag");
}
generate(Shell::Bash, &mut app, appname, &outdir, format!("bash/{}", appname));
generate(Shell::Elvish, &mut app, appname, &outdir, format!("elvish/{}", appname));
generate(Shell::Fish, &mut app, appname, &outdir, format!("fish/{}", appname));
generate(Shell::PowerShell, &mut app, appname, &outdir, format!("powershell/{}", appname));
generate(Shell::Zsh, &mut app, appname, &outdir, format!("zsh/_{}", appname));
}
76 changes: 52 additions & 24 deletions src/archiver.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
use std::fs::{create_dir_all, File};
use std::path::PathBuf;

use crate::cli::{ToatError, Result};
use crate::format::{find_format, Format};
use crate::archiver::zip::ZipArchiver;
use crate::archiver::rar::RarArchiver;
use crate::archiver::tar::{TarArchiver, TarGzArchiver, TarBz2Archiver};
use crate::archiver::sevenz::SevenZArchiver;
use crate::archiver::tar::{TarArchiver, TarBz2Archiver, TarGzArchiver, TarXzArchiver};
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 os;
mod zip;
mod rar;
mod sevenz;
mod tar;
mod zip;

pub trait Archiver {
fn perform(&self, inout: ArchiverOpts) -> Result<()>;
fn perform(&self, inout: &ArchiverOpts) -> Result<()>;
fn format(&self) -> Format;
}

Expand All @@ -24,12 +26,14 @@ pub fn create_archiver(dest: &PathBuf) -> Result<Box<dyn Archiver>> {
match format {
Ok(format) => {
return match format {
Format::Zip => Ok(Box::new(ZipArchiver{})),
Format::Tar => Ok(Box::new(TarArchiver{})),
Format::TarGz => Ok(Box::new(TarGzArchiver{})),
Format::TarBz2 => Ok(Box::new(TarBz2Archiver{})),
Format::Rar => Ok(Box::new(RarArchiver{})),
_ => Err(ToatError::UnsupportedFormat("unsupported format".to_string())),
Format::Zip => Ok(Box::new(ZipArchiver {})),
Format::Tar => Ok(Box::new(TarArchiver {})),
Format::TarGz => Ok(Box::new(TarGzArchiver {})),
Format::TarBz2 => Ok(Box::new(TarBz2Archiver {})),
Format::TarXz => Ok(Box::new(TarXzArchiver {})),
Format::Rar => Ok(Box::new(RarArchiver {})),
Format::SevenZ => Ok(Box::new(SevenZArchiver {})),
_ => Err(ToteError::UnknownFormat(format.to_string())),
}
}
Err(msg) => Err(msg),
Expand All @@ -40,10 +44,12 @@ pub fn archiver_info(archiver: &Box<dyn Archiver>, opts: &ArchiverOpts) -> Strin
format!(
"Format: {:?}\nDestination: {:?}\nTargets: {:?}",
archiver.format(),
opts.destination(),
opts.targets().iter()
opts.dest_path(),
opts.targets()
.iter()
.map(|item| item.to_str().unwrap())
.collect::<Vec<_>>().join(", ")
.collect::<Vec<_>>()
.join(", ")
)
}

Expand All @@ -58,9 +64,7 @@ pub struct ArchiverOpts {
impl ArchiverOpts {
pub fn new(opts: &CliOpts) -> Self {
let args = opts.args.clone();
let dest = opts.output.clone().unwrap_or_else(|| {
PathBuf::from(".")
});
let dest = opts.output.clone().unwrap_or_else(|| PathBuf::from("."));
ArchiverOpts {
dest: dest,
targets: args,
Expand All @@ -71,26 +75,50 @@ impl ArchiverOpts {
}

#[cfg(test)]
pub fn create(dest: PathBuf, targets: Vec<PathBuf>, overwrite: bool, recursive: bool, verbose: bool) -> Self {
ArchiverOpts { dest, targets, overwrite, recursive, v: create_verboser(verbose) }
pub fn create(
dest: PathBuf,
targets: Vec<PathBuf>,
overwrite: bool,
recursive: bool,
verbose: bool,
) -> Self {
ArchiverOpts {
dest,
targets,
overwrite,
recursive,
v: create_verboser(verbose),
}
}

pub fn targets(&self) -> Vec<PathBuf> {
self.targets.clone()
}

/// Simply return the path for destination.
pub fn dest_path(&self) -> PathBuf {
self.dest.clone()
}

/// Returns the destination file for the archive with opening it and create the parent directories.
/// If the path for destination is a directory or exists and overwrite is false,
/// this function returns an error.
pub fn destination(&self) -> Result<File> {
let p = self.dest.as_path();
print!("{:?}: {}\n", p, p.exists());
if p.is_file() && p.exists() && !self.overwrite {
return Err(ToatError::FileExists(self.dest.clone()))
return Err(ToteError::FileExists(self.dest.clone()));
}
if let Some(parent) = p.parent() {
if !parent.exists() {
let _ = create_dir_all(parent);
if let Err(e) = create_dir_all(parent) {
return Err(ToteError::IOError(e));
}
}
}
match File::create(self.dest.as_path()) {
Err(e) => Err(ToatError::IOError(e)),
Ok(f) => Ok(f),
Err(e) => Err(ToteError::IOError(e)),
}
}
}
Expand Down Expand Up @@ -121,4 +149,4 @@ mod tests {
assert!(a5.is_ok());
assert_eq!(a5.unwrap().format(), Format::Rar);
}
}
}
8 changes: 4 additions & 4 deletions src/archiver/rar.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::archiver::{Archiver, Format, ArchiverOpts};
use crate::cli::{ToatError, Result};
use crate::cli::{ToteError, Result};

pub(super) struct RarArchiver {
}

impl Archiver for RarArchiver {
fn perform(&self, _: ArchiverOpts) -> Result<()> {
Err(ToatError::UnsupportedFormat("only extraction support for rar".to_string()))
fn perform(&self, _: &ArchiverOpts) -> Result<()> {
Err(ToteError::UnsupportedFormat("only extraction support for rar".to_string()))
}
fn format(&self) -> Format {
Format::Rar
Expand Down Expand Up @@ -36,7 +36,7 @@ mod tests {
recursive: false,
v: create_verboser(false),
};
let r = archiver.perform(opts);
let r = archiver.perform(&opts);
assert!(r.is_err());
}
}
Loading
Loading