Skip to content

Commit

Permalink
Merge pull request #16 from tamada/20240514
Browse files Browse the repository at this point in the history
implement done
  • Loading branch information
tamada authored May 1, 2024
2 parents af19a5a + 4f088a4 commit 440b8a6
Show file tree
Hide file tree
Showing 24 changed files with 957 additions and 132 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
matrix:
os:
- ubuntu-latest
- windows-latest
# - windows-latest
- macos-latest
steps:
- name: Setup Rust
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ jobs:
# target: aarch64-pc-windows-gnullvm
# artifact_name: ${{ needs.setup.outputs.appname }}.exe
# asset_name: ${{ needs.setup.outputs.appname }}-${{ needs.setup.outputs.tag }}_windows_amd64
- os: ubuntu-latest
target: x86_64-pc-windows-gnu
artifact_name: ${{ needs.setup.outputs.appname }}.exe
asset_name: ${{ needs.setup.outputs.appname }}-${{ needs.setup.outputs.tag }}_windows_arm64
# - os: ubuntu-latest
# target: x86_64-pc-windows-gnu
# artifact_name: ${{ needs.setup.outputs.appname }}.exe
# asset_name: ${{ needs.setup.outputs.appname }}-${{ needs.setup.outputs.tag }}_windows_arm64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: ${{ needs.setup.outputs.appname }}
Expand Down Expand Up @@ -124,4 +124,4 @@ jobs:
asset_path: dist/${{ matrix.asset_name }}.tar.gz
asset_name: ${{ matrix.asset_name }}.tar.gz
asset_content_type: application/x-gzip
upload_url: ${{ needs.setup.outputs.upload_url }}
upload_url: ${{ needs.setup.outputs.upload_url }}
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "totebag"
version = "0.1.18"
version = "0.2.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 @@ -14,7 +14,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bzip2 = "0.4.4"
clap = { version = "4.5.4", features = ["derive"] }
flate2 = "1.0.29"
tar = "0.4.40"
time = "0.3.36"
unrar = "0.5.3"
zip = "1.1.1"

[build-dependencies]
clap = { version = "4.5.4", features = ["derive"] }
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

A tool for archiving files and directories and extracting several archive formats.

## Description
## :speaking_head: Description

There are many archive formats and their tools. The one problem with using each tool is that its interfaces are slightly different.
Then, The `totebag` treats the archive files as the same interface.
The tool can extract archive files and archive files and directories.

## Usage
## :runner: Usage

```sh
totebag [OPTIONS] <ARGUMENTS...>
Expand All @@ -37,7 +37,7 @@ ARGUMENTS
Otherwise, it will archive the files.
```
## Install
## :anchor: Install
```sh
brew install tamada/tap/totebag
Expand Down
168 changes: 54 additions & 114 deletions src/archiver.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,25 @@
use std::{ffi::OsStr, path::PathBuf};
use std::fs::File;
use std::path::PathBuf;

pub trait Archiver {
fn perform(&self, inout: InOut) -> Result<(), String>;
fn format(&self) -> Format;
}
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::verboser::{create_verboser, Verboser};
use crate::CliOpts;

struct ZipArchiver {
}
struct TarArchiver {
}
struct TarGzArchiver {
}
struct TarBz2Archiver {
}
struct RarArchiver {
}
mod optscreator;
mod zip;
mod rar;
mod tar;

impl Archiver for ZipArchiver {
fn perform(&self, inout: InOut) -> Result<(), String> {
Err("not implement yet".to_string())
}
fn format(&self) -> Format {
Format::Zip
}
}
impl Archiver for TarArchiver {
fn perform(&self, inout: InOut) -> Result<(), String> {
Err("not implement yet".to_string())
}
fn format(&self) -> Format {
Format::Tar
}
}
impl Archiver for TarGzArchiver{
fn perform(&self, inout: InOut) -> Result<(), String> {
Err("not implement yet".to_string())
}
fn format(&self) -> Format {
Format::TarGz
}
}
impl Archiver for TarBz2Archiver {
fn perform(&self, inout: InOut) -> Result<(), String> {
Err("not implement yet".to_string())
}
fn format(&self) -> Format {
Format::TarBz2
}
}
impl Archiver for RarArchiver {
fn perform(&self, inout: InOut) -> Result<(), String> {
Err("not implement yet".to_string())
}
fn format(&self) -> Format {
Format::Rar
}
pub trait Archiver {
fn perform(&self, inout: ArchiverOpts) -> Result<()>;
fn format(&self) -> Format;
}

pub fn create_archiver(dest: PathBuf) -> Result<Box<dyn Archiver>, String> {
pub fn create_archiver(dest: PathBuf) -> Result<Box<dyn Archiver>> {
let format = find_format(dest.file_name());
match format {
Ok(format) => {
Expand All @@ -67,85 +29,63 @@ pub fn create_archiver(dest: PathBuf) -> Result<Box<dyn Archiver>, String> {
Format::TarGz => Ok(Box::new(TarGzArchiver{})),
Format::TarBz2 => Ok(Box::new(TarBz2Archiver{})),
Format::Rar => Ok(Box::new(RarArchiver{})),
_ => Err("unsupported format".to_string()),
_ => Err(ToatError::UnsupportedFormat("unsupported format".to_string())),
}
}
Err(msg) => Err(msg),
}
}

pub fn archiver_info(archiver: Box<dyn Archiver>, inout: InOut) -> String {
pub fn archiver_info(archiver: &Box<dyn Archiver>, opts: &ArchiverOpts) -> String {
format!(
"Format: {:?}\nDestination: {:?}\nTargets: {:?}",
archiver.format(),
inout.destination(),
inout.targets().iter()
opts.destination(),
opts.targets().iter()
.map(|item| item.to_str().unwrap())
.collect::<Vec<_>>().join(", ")
)
}

pub struct InOut {
dest: PathBuf,
targets: Vec<PathBuf>,
pub struct ArchiverOpts {
pub dest: PathBuf,
pub targets: Vec<PathBuf>,
pub overwrite: bool,
pub recursive: bool,
pub v: Box<dyn Verboser>,
}

impl InOut {
pub fn new(dest: PathBuf, targets: Vec<PathBuf>) -> Self {
InOut { dest, targets }
impl ArchiverOpts {
pub fn new(opts: &CliOpts) -> Self {
let args = opts.args.clone();
let dest = opts.dest.clone().unwrap_or_else(|| {
PathBuf::from(".")
});
ArchiverOpts {
dest: dest,
targets: args,
overwrite: opts.overwrite,
recursive: !opts.no_recursive,
v: create_verboser(opts.verbose),
}
}

#[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 targets(&self) -> Vec<PathBuf> {
self.targets.clone()
}
pub fn destination(&self) -> &PathBuf {
&self.dest
}
}

fn find_format(file_name: Option<&OsStr>) -> Result<Format, String> {
match file_name {
Some(file_name) => {
let name = file_name.to_str().unwrap().to_lowercase();
if name.ends_with(".tar.gz") || name.ends_with(".tgz") {
return Ok(Format::TarGz);
} else if name.ends_with(".tar.bz2") || name.ends_with(".tbz2") {
return Ok(Format::TarBz2);
} else if name.ends_with(".tar") {
return Ok(Format::Tar);
} else if name.ends_with(".rar") {
return Ok(Format::Rar);
} else if name.ends_with(".zip") {
return Ok(Format::Zip);
} else {
return Ok(Format::Unknown);
}
pub fn destination(&self) -> Result<File> {
let p = self.dest.as_path();
if p.is_file() && p.exists() && !self.overwrite {
return Err(ToatError::FileExists(self.dest.clone()))
}
match File::create(self.dest.as_path()) {
Err(e) => Err(ToatError::IOError(e)),
Ok(f) => Ok(f),
}
None => Err("no file name provided".to_string()),
}
}

#[derive(Debug, PartialEq)]
pub enum Format {
Zip,
Tar,
TarGz,
TarBz2,
Rar,
Unknown,
}

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

#[test]
fn test_format() {
assert!(find_format(None).is_err());
assert_eq!(find_format(Some(OsStr::new("hoge.zip"))), Ok(Format::Zip));
assert_eq!(find_format(Some(OsStr::new("hoge.unknown"))), Ok(Format::Unknown));
assert_eq!(find_format(Some(OsStr::new("hoge.tar"))), Ok(Format::Tar));
assert_eq!(find_format(Some(OsStr::new("hoge.rar"))), Ok(Format::Rar));
assert_eq!(find_format(Some(OsStr::new("hoge.tar.gz"))), Ok(Format::TarGz));
assert_eq!(find_format(Some(OsStr::new("hoge.tar.bz2"))), Ok(Format::TarBz2));
}
}
6 changes: 6 additions & 0 deletions src/archiver/optscreator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[cfg(target_os = "windows")]
pub mod windows;

#[cfg(any(target_os = "linux", target_os = "macos"))]
pub mod linux;

17 changes: 17 additions & 0 deletions src/archiver/optscreator/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;

use time::OffsetDateTime;
use zip::write::SimpleFileOptions;
use zip::DateTime;

pub fn create(target: &PathBuf) -> SimpleFileOptions {
let metadata = std::fs::metadata(&target).unwrap();
let mod_time = DateTime::try_from(
OffsetDateTime::from(metadata.modified().unwrap()));

SimpleFileOptions::default()
.last_modified_time(mod_time.unwrap())
.compression_method(zip::CompressionMethod::Stored)
.unix_permissions(metadata.permissions().mode())
}
9 changes: 9 additions & 0 deletions src/archiver/optscreator/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub fn create(target: &PathBuf) -> SimpleFileOptions {
let metadata = std::fs::metadata(&target).unwrap();
let mod_time = DateTime::try_from(
OffsetDateTime::from(metadata.modified().unwrap()));

SimpleFileOptions::default()
.last_modified_time(mod_time.unwrap())
.compression_method(zip::CompressionMethod::Stored)
}
14 changes: 14 additions & 0 deletions src/archiver/rar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::archiver::{Archiver, Format, ArchiverOpts};
use crate::cli::{ToatError, 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 format(&self) -> Format {
Format::Rar
}
}
Loading

0 comments on commit 440b8a6

Please sign in to comment.