diff --git a/Cargo.toml b/Cargo.toml index 3f44d0b..afcdc9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/__test__/index.spec.ts b/__test__/index.spec.ts index 93351e0..697dc70 100644 --- a/__test__/index.spec.ts +++ b/__test__/index.spec.ts @@ -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') + } +}) diff --git a/__test__/src.tar.bz2 b/__test__/src.tar.bz2 new file mode 100644 index 0000000..c6cfe6c Binary files /dev/null and b/__test__/src.tar.bz2 differ diff --git a/__test__/src.tar.gz b/__test__/src.tar.gz new file mode 100644 index 0000000..978128a Binary files /dev/null and b/__test__/src.tar.gz differ diff --git a/__test__/src.tar.xz b/__test__/src.tar.xz new file mode 100644 index 0000000..6cf20d7 Binary files /dev/null and b/__test__/src.tar.xz differ diff --git a/benchmark/bench.ts b/benchmark/bench.ts index 12be90c..6c6611f 100644 --- a/benchmark/bench.ts +++ b/benchmark/bench.ts @@ -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( @@ -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 }, diff --git a/index.d.ts b/index.d.ts index a9900b7..65df561 100644 --- a/index.d.ts +++ b/index.d.ts @@ -420,5 +420,6 @@ export const enum EntryType { /** Global extended header */ XGlobalHeader = 11, /** Extended Header */ - XHeader = 12, + XHeader = 12 } + diff --git a/index.js b/index.js index 8896c35..2b879b2 100644 --- a/index.js +++ b/index.js @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') diff --git a/package.json b/package.json index 4cd8179..a1521ed 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/package/README.md b/package/README.md deleted file mode 100644 index e680a86..0000000 --- a/package/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `@napi-rs/tar-linux-x64-gnu` - -This is the **x86_64-unknown-linux-gnu** binary for `@napi-rs/tar` diff --git a/package/package.json b/package/package.json deleted file mode 100644 index 3b8c5bb..0000000 --- a/package/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "@napi-rs/tar-linux-x64-gnu", - "version": "0.0.1", - "os": [ - "linux" - ], - "cpu": [ - "x64" - ], - "main": "tar.linux-x64-gnu.node", - "files": [ - "tar.linux-x64-gnu.node" - ], - "description": "Node.js tar binding https://docs.rs/tar/latest/tar/", - "keywords": [ - "napi-rs", - "NAPI", - "N-API", - "Rust", - "node-addon", - "node-addon-api" - ], - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "public" - }, - "repository": "git@github.com:napi-rs/tar.git", - "libc": [ - "glibc" - ] -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 357434e..3cb91b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; @@ -21,21 +21,96 @@ mod header; static GLOBAL: MiMalloc = MiMalloc; pub struct ArchiveSource { - inner: Either>>, + inner: Either4< + File, + Cursor>, + flate2::read::GzDecoder, + bzip2::read::BzDecoder, + >, +} + +enum FileOrBuffer { + File(File), + Buffer(Cursor>), +} + +impl Read for FileOrBuffer { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match self { + Self::File(file) => file.read(buf), + Self::Buffer(buffer) => buffer.read(buf), + } + } } impl ArchiveSource { fn from_node_input(input: Either) -> Result { 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())), - }), } } } @@ -43,8 +118,10 @@ impl ArchiveSource { impl Read for ArchiveSource { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 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), } } }