Skip to content

Commit

Permalink
feat: additional error properties (#11)
Browse files Browse the repository at this point in the history
* chore: remove rustfmt.toml as it is incorrect

* refactor: fix format

* chore: upgrade rust-toolchain

* chore: update dependencies

* chore(git): add Cargo.lock

* chore: don't force editor customization

* ci: fix

* ci: fix

* chore(typescript): regenerate index.d.ts

* refactor: prefer `map_err`

* refactor: replace `extern crate` with `use`

* feat: additional error properties

* test: fix failure case

* test: fix failure case

* fix: error construction

* fix: ENOENT detection

* feat: generate index.d.ts

* feat: stop exposing binding

* docs: fix grammar

* test: error codes

* fix: scripts

* fix: correct package.json#files

* refactor: remove `throw`

Co-authored-by: Copilot <[email protected]>

---------

Co-authored-by: Copilot <[email protected]>
  • Loading branch information
KSXGitHub and Copilot authored Nov 22, 2024
1 parent a07534d commit 313c935
Show file tree
Hide file tree
Showing 10 changed files with 449 additions and 286 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ futures = "0.3.31"
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.16.13", default-features = false, features = ["napi4"] }
napi-derive = "2.16.12"
pipe-trait = "0.4.0"

[build-dependencies]
napi-build = "2.1.3"
Expand Down
39 changes: 32 additions & 7 deletions __test__/main.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { afterAll, describe, expect, it } from 'vitest';
import { constants } from 'os';
import { join, resolve } from 'path';
import { mkdir, rm, writeFile } from 'fs/promises';
import { readFileSync } from 'fs';
Expand Down Expand Up @@ -98,12 +99,20 @@ describe('reflink', () => {
const dir = sandboxDir();
await prepare(dir);

expect(() => {
try {
reflinkFileSync(
join(dir, 'file-does-not-exist.txt'),
join(dir, 'file1-copy.txt')
);
}).toThrow();
} catch (error) {
expect(error).toMatchObject({
message: expect.any(String),
code: 'ENOENT',
errno: constants.errno.ENOENT,
});
return;
}
throw new Error('Expecting an error, but none was thrown');
});

it('should fail if the source file does not exist (async)', async () => {
Expand All @@ -115,24 +124,40 @@ describe('reflink', () => {
join(dir, 'file-does-not-exist.txt'),
join(dir, 'file1-copy.txt')
)
).rejects.toThrow();
).rejects.toMatchObject({
message: expect.any(String),
code: 'ENOENT',
errno: constants.errno.ENOENT,
});
});

it('should fail if the destination file already exists (sync)', async () => {
const dir = sandboxDir();
const sandboxFiles = await prepare(dir);

expect(() => {
try {
reflinkFileSync(sandboxFiles[0].path, sandboxFiles[1].path);
}).toThrow();
} catch (error) {
expect(error).toMatchObject({
message: expect.any(String),
code: 'EEXIST',
errno: constants.errno.EEXIST,
});
return;
}
throw new Error('Expecting an error, but none was thrown');
});

it('should fail if the destination file already exists (async)', async () => {
const dir = sandboxDir();
const sandboxFiles = await prepare(dir);
await expect(
reflinkFile(sandboxFiles[0].path, sandboxFiles[1].path)
).rejects.toThrow();
).rejects.toMatchObject({
message: expect.any(String),
code: 'EEXIST',
errno: constants.errno.EEXIST,
});
});

it('should fail if the source file is a directory (sync)', async () => {
Expand Down Expand Up @@ -330,7 +355,7 @@ describe('reflink', () => {
});

/**
* The issue with empty cloned files doesnt seem related to ASCII characters
* The issue with empty cloned files doesn't seem related to ASCII characters
*/
it.skip('should clone "ascii-file.js" file correctly (sync)', async () => {
const dir = sandboxDir();
Expand Down
16 changes: 16 additions & 0 deletions binding.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* tslint:disable */
/* eslint-disable */

/* auto-generated by NAPI-RS */

export declare function reflinkFile(src: string, dst: string): Promise<number | ReflinkError>
export declare function reflinkFileSync(src: string, dst: string): number | ReflinkError
/** Contains all properties to construct an actual error. */
export class ReflinkError {
message: string
path: string
dest: string
code?: string
errno?: number
constructor(message: string, path: string, dest: string, code?: string, errno?: number)
}
259 changes: 259 additions & 0 deletions binding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */

/* auto-generated by NAPI-RS */

const { existsSync, readFileSync } = require('fs')
const { join } = require('path')

const { platform, arch } = process

let nativeBinding = null
let localFileExisted = false
let loadError = null

function isMusl() {
// For Node 10
if (!process.report || typeof process.report.getReport !== 'function') {
try {
const lddPath = require('child_process').execSync('which ldd').toString().trim()
return readFileSync(lddPath, 'utf8').includes('musl')
} catch (e) {
return true
}
} else {
const { glibcVersionRuntime } = process.report.getReport().header
return !glibcVersionRuntime
}
}

switch (platform) {
case 'android':
switch (arch) {
case 'arm64':
localFileExisted = existsSync(join(__dirname, 'reflink.android-arm64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./reflink.android-arm64.node')
} else {
nativeBinding = require('@reflink/reflink-android-arm64')
}
} catch (e) {
loadError = e
}
break
case 'arm':
localFileExisted = existsSync(join(__dirname, 'reflink.android-arm-eabi.node'))
try {
if (localFileExisted) {
nativeBinding = require('./reflink.android-arm-eabi.node')
} else {
nativeBinding = require('@reflink/reflink-android-arm-eabi')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Android ${arch}`)
}
break
case 'win32':
switch (arch) {
case 'x64':
localFileExisted = existsSync(
join(__dirname, 'reflink.win32-x64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.win32-x64-msvc.node')
} else {
nativeBinding = require('@reflink/reflink-win32-x64-msvc')
}
} catch (e) {
loadError = e
}
break
case 'ia32':
localFileExisted = existsSync(
join(__dirname, 'reflink.win32-ia32-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.win32-ia32-msvc.node')
} else {
nativeBinding = require('@reflink/reflink-win32-ia32-msvc')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'reflink.win32-arm64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.win32-arm64-msvc.node')
} else {
nativeBinding = require('@reflink/reflink-win32-arm64-msvc')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Windows: ${arch}`)
}
break
case 'darwin':
localFileExisted = existsSync(join(__dirname, 'reflink.darwin-universal.node'))
try {
if (localFileExisted) {
nativeBinding = require('./reflink.darwin-universal.node')
} else {
nativeBinding = require('@reflink/reflink-darwin-universal')
}
break
} catch {}
switch (arch) {
case 'x64':
localFileExisted = existsSync(join(__dirname, 'reflink.darwin-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./reflink.darwin-x64.node')
} else {
nativeBinding = require('@reflink/reflink-darwin-x64')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'reflink.darwin-arm64.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.darwin-arm64.node')
} else {
nativeBinding = require('@reflink/reflink-darwin-arm64')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on macOS: ${arch}`)
}
break
case 'freebsd':
if (arch !== 'x64') {
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
}
localFileExisted = existsSync(join(__dirname, 'reflink.freebsd-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./reflink.freebsd-x64.node')
} else {
nativeBinding = require('@reflink/reflink-freebsd-x64')
}
} catch (e) {
loadError = e
}
break
case 'linux':
switch (arch) {
case 'x64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'reflink.linux-x64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.linux-x64-musl.node')
} else {
nativeBinding = require('@reflink/reflink-linux-x64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'reflink.linux-x64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.linux-x64-gnu.node')
} else {
nativeBinding = require('@reflink/reflink-linux-x64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'reflink.linux-arm64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.linux-arm64-musl.node')
} else {
nativeBinding = require('@reflink/reflink-linux-arm64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'reflink.linux-arm64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.linux-arm64-gnu.node')
} else {
nativeBinding = require('@reflink/reflink-linux-arm64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm':
localFileExisted = existsSync(
join(__dirname, 'reflink.linux-arm-gnueabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./reflink.linux-arm-gnueabihf.node')
} else {
nativeBinding = require('@reflink/reflink-linux-arm-gnueabihf')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Linux: ${arch}`)
}
break
default:
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
}

if (!nativeBinding) {
if (loadError) {
throw loadError
}
throw new Error(`Failed to load native binding`)
}

const { ReflinkError, reflinkFile, reflinkFileSync } = nativeBinding

module.exports.ReflinkError = ReflinkError
module.exports.reflinkFile = reflinkFile
module.exports.reflinkFileSync = reflinkFileSync
Loading

0 comments on commit 313c935

Please sign in to comment.