Skip to content

Commit

Permalink
feat: support new from compressed archive (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn authored Dec 13, 2023
1 parent d0653de commit f3ff770
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 65 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ crate-type = ["cdylib"]

[dependencies]
anyhow = "1"
bzip2 = "0.4"
flate2 = "1"
lzma-rs = { version = "0.3", features = ["stream"] }
infer = "0.15"
napi = { version = "2", features = ["anyhow", "napi6"] }
napi-derive = "2"
tar = "0.4"
Expand Down
21 changes: 21 additions & 0 deletions __test__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,24 @@ test('should be able to create archive from Buffer', async (t) => {
t.is(typeof entry.path(), 'string')
}
})

test('should be able to handle tar.gz', (t) => {
const archive = new Archive(join(__dirname, 'src.tar.gz'))
for (const entry of archive.entries()) {
t.is(typeof entry.path(), 'string')
}
})

test('should be able to handle tar.bz2', (t) => {
const archive = new Archive(join(__dirname, 'src.tar.bz2'))
for (const entry of archive.entries()) {
t.is(typeof entry.path(), 'string')
}
})

test('should be able to handle tar.xz', (t) => {
const archive = new Archive(join(__dirname, 'src.tar.xz'))
for (const entry of archive.entries()) {
t.is(typeof entry.path(), 'string')
}
})
Binary file added __test__/src.tar.bz2
Binary file not shown.
Binary file added __test__/src.tar.gz
Binary file not shown.
Binary file added __test__/src.tar.xz
Binary file not shown.
18 changes: 16 additions & 2 deletions benchmark/bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,21 @@ import { list } from 'tar'

import { Archive } from '../index'

const ARCHIVE_PATH = join(__dirname, '..', '__test__', 'src.tar')
const ARCHIVE_PATH = join(__dirname, '..', '__test__', 'src.tar.gz')

list({
file: join(__dirname, '..', '__test__', 'src.tar.gz'),
onentry: (entry) => {
console.info('list from node-tar', entry.path)
},
sync: true,
})

const archiveBuffer = readFileSync(ARCHIVE_PATH)
const archive = new Archive(archiveBuffer)
for (const entry of archive.entries()) {
console.info('list from @napi-rs/tar', entry.path())
}

async function run() {
await b.suite(
Expand All @@ -22,7 +36,7 @@ async function run() {

b.add('node-tar', () => {
list({
file: join(__dirname, '..', '__test__', 'src.tar'),
file: join(__dirname, '..', '__test__', 'src.tar.gz'),
onentry: (entry) => {
entry.path
},
Expand Down
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,5 +420,6 @@ export const enum EntryType {
/** Global extended header */
XGlobalHeader = 11,
/** Extended Header */
XHeader = 12,
XHeader = 12
}

36 changes: 27 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ switch (platform) {
case 'win32':
switch (arch) {
case 'x64':
localFileExisted = existsSync(join(__dirname, 'tar.win32-x64-msvc.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.win32-x64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.win32-x64-msvc.node')
Expand All @@ -106,7 +108,9 @@ switch (platform) {
}
break
case 'ia32':
localFileExisted = existsSync(join(__dirname, 'tar.win32-ia32-msvc.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.win32-ia32-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.win32-ia32-msvc.node')
Expand All @@ -118,7 +122,9 @@ switch (platform) {
}
break
case 'arm64':
localFileExisted = existsSync(join(__dirname, 'tar.win32-arm64-msvc.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.win32-arm64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.win32-arm64-msvc.node')
Expand Down Expand Up @@ -157,7 +163,9 @@ switch (platform) {
}
break
case 'arm64':
localFileExisted = existsSync(join(__dirname, 'tar.darwin-arm64.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.darwin-arm64.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.darwin-arm64.node')
Expand Down Expand Up @@ -191,7 +199,9 @@ switch (platform) {
switch (arch) {
case 'x64':
if (isMusl()) {
localFileExisted = existsSync(join(__dirname, 'tar.linux-x64-musl.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.linux-x64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.linux-x64-musl.node')
Expand All @@ -202,7 +212,9 @@ switch (platform) {
loadError = e
}
} else {
localFileExisted = existsSync(join(__dirname, 'tar.linux-x64-gnu.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.linux-x64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.linux-x64-gnu.node')
Expand All @@ -216,7 +228,9 @@ switch (platform) {
break
case 'arm64':
if (isMusl()) {
localFileExisted = existsSync(join(__dirname, 'tar.linux-arm64-musl.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.linux-arm64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.linux-arm64-musl.node')
Expand All @@ -227,7 +241,9 @@ switch (platform) {
loadError = e
}
} else {
localFileExisted = existsSync(join(__dirname, 'tar.linux-arm64-gnu.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.linux-arm64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.linux-arm64-gnu.node')
Expand All @@ -240,7 +256,9 @@ switch (platform) {
}
break
case 'arm':
localFileExisted = existsSync(join(__dirname, 'tar.linux-arm-gnueabihf.node'))
localFileExisted = existsSync(
join(__dirname, 'tar.linux-arm-gnueabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./tar.linux-arm-gnueabihf.node')
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
"scripts": {
"artifacts": "napi artifacts",
"bench": "node -r @swc-node/register benchmark/bench.ts",
"build": "napi build --platform --release --pipe \"prettier -w\"",
"build:debug": "napi build --platform --pipe \"prettier -w\"",
"build": "napi build --platform --release",
"build:debug": "napi build --platform",
"format": "run-p format:prettier format:rs format:toml",
"format:prettier": "prettier . -w",
"format:toml": "taplo format",
Expand Down
3 changes: 0 additions & 3 deletions package/README.md

This file was deleted.

35 changes: 0 additions & 35 deletions package/package.json

This file was deleted.

103 changes: 90 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

use std::{
fs::File,
io::{Cursor, Read},
io::{BufReader, Cursor, Read},
};

use mimalloc::MiMalloc;
use napi::{
bindgen_prelude::{Env, Reference},
bindgen_prelude::{Either4, Env, Reference},
Either, JsBuffer,
};
use napi_derive::napi;
Expand All @@ -21,30 +21,107 @@ mod header;
static GLOBAL: MiMalloc = MiMalloc;

pub struct ArchiveSource {
inner: Either<File, Cursor<Vec<u8>>>,
inner: Either4<
File,
Cursor<Vec<u8>>,
flate2::read::GzDecoder<FileOrBuffer>,
bzip2::read::BzDecoder<FileOrBuffer>,
>,
}

enum FileOrBuffer {
File(File),
Buffer(Cursor<Vec<u8>>),
}

impl Read for FileOrBuffer {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
Self::File(file) => file.read(buf),
Self::Buffer(buffer) => buffer.read(buf),
}
}
}

impl ArchiveSource {
fn from_node_input(input: Either<String, JsBuffer>) -> Result<Self, napi::Error> {
match input {
Either::A(path) => {
let file = File::open(path)?;
Ok(Self {
inner: Either::A(file),
})
Either::A(path) => match infer::get_from_path(&path)?.map(|s| s.extension()) {
Some("tar") => {
let file = File::open(&path)?;
Ok(Self {
inner: Either4::A(file),
})
}
Some("bz2") => {
let file = File::open(&path)?;
let bz2 = bzip2::read::BzDecoder::new(FileOrBuffer::File(file));
Ok(Self {
inner: Either4::D(bz2),
})
}
Some("xz") => {
let mut file = BufReader::new(File::open(&path)?);
let mut output = Vec::new();
lzma_rs::xz_decompress(&mut file, &mut output).map_err(anyhow::Error::from)?;
Ok(Self {
inner: Either4::B(Cursor::new(output)),
})
}
Some("gz") => {
let file = File::open(&path)?;
Ok(Self {
inner: Either4::C(flate2::read::GzDecoder::new(FileOrBuffer::File(file))),
})
}
_ => Err(napi::Error::new(
napi::Status::InvalidArg,
format!("Unsupported file type for {}", path),
)),
},
Either::B(buffer) => {
let value = buffer.into_value()?;
match infer::get(value.as_ref()).map(|s| s.extension()) {
Some("tar") => Ok(Self {
inner: Either4::B(Cursor::new(value.to_vec())),
}),
Some("bz2") => {
let bz2 =
bzip2::read::BzDecoder::new(FileOrBuffer::Buffer(Cursor::new(value.to_vec())));
Ok(Self {
inner: Either4::D(bz2),
})
}
Some("xz") => {
let mut input = value.as_ref();
let mut output = Vec::new();
lzma_rs::xz_decompress(&mut input, &mut output).map_err(anyhow::Error::from)?;
Ok(Self {
inner: Either4::B(Cursor::new(output)),
})
}
Some("gz") => Ok(Self {
inner: Either4::C(flate2::read::GzDecoder::new(FileOrBuffer::Buffer(
Cursor::new(value.to_vec()),
))),
}),
_ => Err(napi::Error::new(
napi::Status::InvalidArg,
"Unsupported file type for input ",
)),
}
}
Either::B(buffer) => Ok(Self {
inner: Either::B(Cursor::new(buffer.into_value()?.to_vec())),
}),
}
}
}

impl Read for ArchiveSource {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match &mut self.inner {
Either::A(file) => file.read(buf),
Either::B(buffer) => buffer.read(buf),
Either4::A(file) => file.read(buf),
Either4::B(buffer) => buffer.read(buf),
Either4::C(gz) => gz.read(buf),
Either4::D(bz2) => bz2.read(buf),
}
}
}
Expand Down

0 comments on commit f3ff770

Please sign in to comment.