Skip to content

Commit

Permalink
feat: 🎸 add ability to close files
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 20, 2023
1 parent 15b9e2c commit 0db56be
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 18 deletions.
27 changes: 20 additions & 7 deletions src/fsa-to-node/FsaNodeFs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import {
createError,
flagsToNumber,
genRndStr6,
isFd,
modeToNumber,
nullCheck,
pathToFilename,
validateCallback,
validateFd,
} from '../node/util';
import { pathToLocation } from './util';
import { MODE } from '../node/constants';
import { ERRSTR, MODE } from '../node/constants';
import { strToEncoding } from '../encoding';
import { FsaToNodeConstants } from './constants';
import { bufferToEncoding } from '../volume';
Expand Down Expand Up @@ -77,12 +79,15 @@ export class FsaNodeFs implements FsCallbackApi {
return file;
}

private async getFileByFd(fd: number, funcName?: string): Promise<FsaNodeFsOpenFile> {
if (!isFd(fd)) throw TypeError(ERRSTR.FD);
const file = this.fds.get(fd);
if (!file) throw createError('EBADF', funcName);
return file;
}

private async getFileById(id: misc.TFileId, funcName?: string): Promise<fsa.IFileSystemFileHandle> {
if (typeof id === 'number') {
const file = this.fds.get(id);
if (!file) throw createError('EBADF', funcName);
return file.file;
}
if (typeof id === 'number') return (await this.getFileByFd(id, funcName)).file;
const filename = pathToFilename(id);
const [folder, name] = pathToLocation(filename);
return await this.getFile(folder, name, funcName);
Expand Down Expand Up @@ -116,7 +121,15 @@ export class FsaNodeFs implements FsCallbackApi {
};

public readonly close: FsCallbackApi['close'] = (fd: number, callback: misc.TCallback<void>): void => {
throw new Error('Not implemented');
validateFd(fd);
this.getFileByFd(fd, 'close')
.then(file => file.close())
.then(() => {
this.fds.delete(fd);
callback(null);
}, error => {
callback(error);
});
};

public readonly read: FsCallbackApi['read'] = (
Expand Down
2 changes: 2 additions & 0 deletions src/fsa-to-node/FsaNodeFsOpenFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export class FsaNodeFsOpenFile {
public readonly flags: number,
public readonly file: fsa.IFileSystemFileHandle,
) {}

public async close(): Promise<void> {}
}
39 changes: 36 additions & 3 deletions src/fsa-to-node/__tests__/FsaNodeFs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,43 @@ describe('.readFile()', () => {
expect(data.toString()).toBe('test');
});

test('can read file by file descriptor', async () => {
test('can read file by file handle', async () => {
const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null });
const fd = await fs.promises.open('/folder/file');
const data = await fs.promises.readFile(fd);
const handle = await fs.promises.open('/folder/file');
expect(typeof handle).toBe('object');
const data = await fs.promises.readFile(handle);
expect(data.toString()).toBe('test');
});

test('can read file by file descriptor', async () => {
const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null });
const fd = await new Promise<number>(resolve => {
fs.open('/folder/file', 'r', (err, fd) => resolve(fd!));
});
expect(typeof fd).toBe('number');
const data = await new Promise<string>(resolve => {
fs.readFile(fd, {encoding: 'utf8'}, (err, data) => resolve(data as string));
});
expect(data).toBe('test');
});

test('cannot read from closed file descriptor', async () => {
const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null });
const fd = await new Promise<number>(resolve => {
fs.open('/folder/file', 'r', (err, fd) => resolve(fd!));
});
expect(typeof fd).toBe('number');
await new Promise<void>(resolve => {
fs.close(fd, () => resolve());
});
try {
await new Promise<string>((resolve, reject) => {
fs.readFile(fd, {encoding: 'utf8'}, (err, data) => reject(err));
});
throw new Error('Expected error');
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.code).toBe('EBADF');
}
});
});
8 changes: 8 additions & 0 deletions src/node/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,11 @@ export function flagsToNumber(flags: misc.TFlags | undefined): number {
// throw new TypeError(formatError(ERRSTR_FLAG(flags)));
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'flags', flags);
}

export function isFd(path): boolean {
return path >>> 0 === path;
}

export function validateFd(fd) {
if (!isFd(fd)) throw TypeError(ERRSTR.FD);
}
10 changes: 2 additions & 8 deletions src/volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
createError,
genRndStr6,
flagsToNumber,
validateFd,
isFd,
} from './node/util';
import type { PathLike, symlink } from 'fs';

Expand Down Expand Up @@ -243,14 +245,6 @@ export function bufferToEncoding(buffer: Buffer, encoding?: TEncodingExtended):
else return buffer.toString(encoding);
}

function isFd(path): boolean {
return path >>> 0 === path;
}

function validateFd(fd) {
if (!isFd(fd)) throw TypeError(ERRSTR.FD);
}

// converts Date or number to a fractional UNIX timestamp
export function toUnixTimestamp(time) {
// tslint:disable-next-line triple-equals
Expand Down

0 comments on commit 0db56be

Please sign in to comment.