Skip to content

Commit

Permalink
Merge pull request #19 from tamada/release/v0.3.0
Browse files Browse the repository at this point in the history
Release/v0.3.0
  • Loading branch information
tamada authored May 14, 2024
2 parents 93ace08 + 8b2d291 commit ce3b57a
Show file tree
Hide file tree
Showing 17 changed files with 579 additions and 209 deletions.
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

0 comments on commit ce3b57a

Please sign in to comment.