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

implement done #16

Merged
merged 24 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
71e3537
update README to add emoji for each headings
tamada Apr 23, 2024
872cc9b
test publish github action script
tamada Apr 14, 2024
ef7f1df
update github action script
tamada Apr 14, 2024
9a29f3e
update
tamada Apr 15, 2024
1bdb6b8
update publish.yaml
tamada Apr 15, 2024
8c0dd98
udpate publish.yaml for fixing CI errors
tamada Apr 15, 2024
1406400
udpate publish.yaml for fixing CI errors
tamada Apr 15, 2024
1bdd6ae
fix CI errors of invalid reference of upload_url
tamada Apr 15, 2024
afbc0c5
fix CI errors of invalid reference of upload_url
tamada Apr 15, 2024
634b9e9
update
tamada Apr 17, 2024
14e28fb
introduce cargo-bininstall
tamada Apr 17, 2024
27a87e1
update rust cross installation
tamada Apr 17, 2024
ed93172
change cross platform environment to use taiki-e/cross-toolchain-action
tamada Apr 17, 2024
729aa48
fix the invalid github actions workflow
tamada Apr 17, 2024
955bcd9
update cross compile environment
tamada Apr 17, 2024
8f2722f
fix the typo of architecture name
tamada Apr 17, 2024
ed46328
implement zip archiver
tamada Apr 27, 2024
f9272ea
implement tar archiver
tamada Apr 27, 2024
5543c0c
implement tar+{gzip, bzip2} archiver
tamada Apr 27, 2024
6e87cb2
implement extracting and listing rar, zip, tar, tar+gz, and tar+bz2
tamada Apr 30, 2024
6222dfe
fix the warnings
tamada Apr 30, 2024
2565a24
fix the test failure for reviwing the archive files and update the codes
tamada May 1, 2024
6523f0e
ignore windows platform for releasing and compilation because the com…
tamada May 1, 2024
4f088a4
fix for merging main
tamada May 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading