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

rework the api #6

Merged
merged 1 commit into from
Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ jobs:
name: Check
runs-on: ubuntu-latest
steps:
- name: Install Dependencies
run: sudo apt-get update; sudo apt-get install libarchive-dev

- name: Checkout sources
uses: actions/checkout@v1

Expand All @@ -26,6 +29,9 @@ jobs:
name: Test Suite
runs-on: ubuntu-latest
steps:
- name: Install Dependencies
run: sudo apt-get update; sudo apt-get install libarchive-dev

- name: Checkout sources
uses: actions/checkout@v1

Expand All @@ -45,6 +51,9 @@ jobs:
name: Lints
runs-on: ubuntu-latest
steps:
- name: Install Dependencies
run: sudo apt-get update; sudo apt-get install libarchive-dev

- name: Checkout sources
uses: actions/checkout@v1

Expand Down
14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ keywords = ["compression", "archive"]
license = "MIT OR Apache-2.0"
readme = "README.md"
edition = "2018"
build = "build.rs"
build = "src/build.rs"

[badges]
travis-ci = { repository = "OSSystems/compress-tools-rs" }

[dependencies]
pipers = "1"
derive_more = "0.99"
thiserror = "1"
nix = "0.17.0"

[build-dependencies]
pkg-config = "0.3.17"

[dev-dependencies]
exitfailure = "0.5"
structopt = "0.2"
tempfile = "3"
tempfile = "3.1.0"
argh = "0.1.3"
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,29 @@

The library provides tools for handling compressed and archive files.

## Examples
## Examples Uncompress
### Archive
```rust
let dir = tempfile::tempdir().unwrap();
uncompress("tests/fixtures/tree.tar.gz", dir.path(), Kind::TarGZip).unwrap();
let dir = tempfile::TempDir::new().expect("Failed to create the tmp directory");
let mut source = std::fs::File::open("tests/fixtures/tree.tar").unwrap();

uncompress_archive(&mut source, dir.path())?;
```

### Archive file
```rust
let mut source = std::fs::File::open("tests/fixtures/tree.tar").unwrap();
let mut target = Vec::default();

uncompress_archive_file(&mut source, &mut target, &"tree/branch2/leaf")?;
```

### File
```rust
let mut source = std::fs::File::open("tests/fixtures/file.txt.gz").unwrap();
let mut target = Vec::default();

uncompress_file(&mut source, &mut target)?;
```

## License
Expand Down
73 changes: 73 additions & 0 deletions binding_script.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// cargo-deps: bindgen = "0.51.1", pkg-config = "0.3.17"

use std::path::PathBuf;

fn main() {
Asakiz marked this conversation as resolved.
Show resolved Hide resolved
let mut lib = pkg_config::Config::new()
.atleast_version("3.2.2")
.probe("libarchive")
.expect("Fail to detect the libarchive library");

let include_path = lib
.include_paths
.pop()
.unwrap_or(PathBuf::from("usr/include"));

let bindings = bindgen::Builder::default()
// Set rustfmt setting
.rustfmt_configuration_file(Some(".rustfmt.toml".into()))

// Set include path
.header(format!("{}/archive.h", include_path.display()))
.header(format!("{}/archive_entry.h", include_path.display()))

// We need to add this as raw_line to pass cargo clippy warning about
// convert to upper camel case
.raw_line("#![allow(non_camel_case_types)]\n")

// We need to add this as raw_line otherwise bindgen generates this as
// u32, causing type mismatch
.raw_line("pub const ARCHIVE_EOF: i32 = 1;")
.raw_line("pub const ARCHIVE_OK: i32 = 0;")

// Binding whitelist
.whitelist_var("ARCHIVE_EXTRACT_TIME")
.whitelist_var("ARCHIVE_EXTRACT_PERM")
.whitelist_var("ARCHIVE_EXTRACT_ACL")
.whitelist_var("ARCHIVE_EXTRACT_FFLAGS")
.whitelist_var("ARCHIVE_EXTRACT_OWNER")
.whitelist_var("ARCHIVE_EXTRACT_FFLAGS")
.whitelist_var("ARCHIVE_EXTRACT_XATTR")
.whitelist_function("archive_read_new")
.whitelist_function("archive_read_support_filter_all")
.whitelist_function("archive_read_support_format_all")
.whitelist_function("archive_read_support_format_raw")
.whitelist_function("archive_read_close")
.whitelist_function("archive_read_free")
.whitelist_function("archive_read_data_block")
.whitelist_function("archive_read_next_header")
.whitelist_function("archive_read_open")
.whitelist_function("archive_write_disk_new")
.whitelist_function("archive_write_disk_set_options")
.whitelist_function("archive_write_disk_set_standard_lookup")
.whitelist_function("archive_write_header")
.whitelist_function("archive_write_finish_entry")
.whitelist_function("archive_write_data_block")
.whitelist_function("archive_write_close")
.whitelist_function("archive_write_free")
.whitelist_function("archive_entry_pathname")
.whitelist_function("archive_entry_free")
.whitelist_function("archive_entry_set_pathname")
.whitelist_function("archive_entry_set_hardlink")
.whitelist_function("archive_entry_hardlink")
.whitelist_function("archive_set_error")
.whitelist_function("archive_error_string")
.generate()
.expect("Unable to generate bindings");

bindings
.write_to_file(PathBuf::from("src/ffi.rs"))
.expect("Couldn't write bindings!");

println!("Sucessfully generated bindings for libarchive.");
}
otavio marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 0 additions & 5 deletions build.rs

This file was deleted.

106 changes: 80 additions & 26 deletions examples/uncompress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,90 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use compress_tools::{uncompress, Result};
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "uncompress")]
enum Opt {
/// Increase the verboseness level
#[structopt(name = "tar_gz")]
TarGz(Args),
#[structopt(name = "tar_xz")]
TarXz(Args),
#[structopt(name = "tar")]
Tar(Args),
use argh::FromArgs;
use compress_tools::*;
use std::path::Path;

#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
#[argh(subcommand)]
nested: CmdLine,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum CmdLine {
UncompressFile(SubCommandUncompressFile),
UncompressArchiveFile(SubCommandUncompressArchiveFile),
UncompressArchive(SubCommandUncompressArchive),
}

#[derive(FromArgs, PartialEq, Debug)]
/// Uncompress subcommand.
#[argh(subcommand, name = "uncompress-file")]
struct SubCommandUncompressFile {
/// source path
#[argh(positional)]
source_path: String,

/// target path
#[argh(positional)]
target_path: String,
}

#[derive(StructOpt, Debug)]
#[structopt(name = "compression-tools")]
struct Args {
/// compressed file to use as input
input: PathBuf,
#[derive(FromArgs, PartialEq, Debug)]
/// Uncompress archive file subcommand.
#[argh(subcommand, name = "uncompress-archive-file")]
struct SubCommandUncompressArchiveFile {
/// source path
#[argh(positional)]
source_path: String,

/// Path to output the uncompressed result
output: PathBuf,
/// target path
#[argh(positional)]
target_path: String,

/// target file
#[argh(positional)]
target_file: String,
}

fn main() -> Result<()> {
match Opt::from_args() {
Opt::TarGz(arg) => uncompress(arg.input, arg.output, compress_tools::Kind::TarGZip),
Opt::TarXz(arg) => uncompress(arg.input, arg.output, compress_tools::Kind::TarXz),
Opt::Tar(arg) => uncompress(arg.input, arg.output, compress_tools::Kind::Tar),
#[derive(FromArgs, PartialEq, Debug)]
/// Uncompress archive subcommand.
#[argh(subcommand, name = "uncompress-archive")]
struct SubCommandUncompressArchive {
/// source path
#[argh(positional)]
source_path: String,

/// target path
#[argh(positional)]
target_path: String,
}

fn main() -> compress_tools::Result<()> {
let cmd: TopLevel = argh::from_env();

match cmd.nested {
CmdLine::UncompressFile(input) => {
let mut source = std::fs::File::open(input.source_path)?;
let mut target = std::fs::File::open(input.target_path)?;

uncompress_file(&mut source, &mut target)?;
}
CmdLine::UncompressArchiveFile(input) => {
let mut source = std::fs::File::open(input.source_path)?;
let mut target = std::fs::File::open(input.target_path)?;

uncompress_archive_file(&mut source, &mut target, &input.target_file)?;
}
CmdLine::UncompressArchive(input) => {
let mut source = std::fs::File::open(input.source_path)?;

uncompress_archive(&mut source, Path::new(&input.target_path))?;
}
}

Ok(())
}
8 changes: 8 additions & 0 deletions src/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
// This forces the tests to run in a single thread. This is
// required for use of the mocks as we run mocked binaries.
pkg_config::Config::new()
.atleast_version("3.2.2")
.probe("libarchive")
otavio marked this conversation as resolved.
Show resolved Hide resolved
.expect("Fail to detect the libarchive library");
}
57 changes: 57 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::ffi;
use std::ffi::CStr;
use thiserror::Error;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Error, Debug)]
pub enum Error {
#[error("Extraction error: '{0}'")]
ExtractionError(String),

#[error("Nix error: '{0}'")]
Nix(#[from] nix::Error),

#[error("Io error: '{0}'")]
Io(#[from] std::io::Error),

#[error("Utf error: '{0}'")]
Utf(#[from] std::str::Utf8Error),

#[error("Try from int error: '{0}'")]
TryInt(#[from] std::num::TryFromIntError),

#[error("Error to create the archive struct, is null")]
ArchiveNull,

#[error("The entry is null, failed to set the pathname")]
EntryNull,

#[error("File not found")]
FileNotFound,

#[error("The end of file")]
EndOfFile,
}

pub(crate) fn archive_result(value: i32, archive: *mut ffi::archive) -> Result<()> {
if value != ffi::ARCHIVE_OK {
return Err(Error::from(archive));
}

Ok(())
}

#[allow(clippy::not_unsafe_ptr_arg_deref)]
impl From<*mut ffi::archive> for Error {
fn from(input: *mut ffi::archive) -> Self {
unsafe {
let input = ffi::archive_error_string(input);
Error::ExtractionError(CStr::from_ptr(input).to_string_lossy().to_string())
}
}
Asakiz marked this conversation as resolved.
Show resolved Hide resolved
}
Loading