diff options
Diffstat (limited to 'lib/internal/fs/dir.js')
-rw-r--r-- | lib/internal/fs/dir.js | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/lib/internal/fs/dir.js b/lib/internal/fs/dir.js new file mode 100644 index 0000000000..c1a25f0de3 --- /dev/null +++ b/lib/internal/fs/dir.js @@ -0,0 +1,201 @@ +'use strict'; + +const { Object } = primordials; + +const pathModule = require('path'); +const binding = internalBinding('fs'); +const dirBinding = internalBinding('fs_dir'); +const { + codes: { + ERR_DIR_CLOSED, + ERR_INVALID_CALLBACK, + ERR_MISSING_ARGS + } +} = require('internal/errors'); + +const { FSReqCallback } = binding; +const internalUtil = require('internal/util'); +const { + getDirent, + getOptions, + getValidatedPath, + handleErrorFromBinding +} = require('internal/fs/utils'); + +const kDirHandle = Symbol('kDirHandle'); +const kDirPath = Symbol('kDirPath'); +const kDirClosed = Symbol('kDirClosed'); +const kDirOptions = Symbol('kDirOptions'); +const kDirReadPromisified = Symbol('kDirReadPromisified'); +const kDirClosePromisified = Symbol('kDirClosePromisified'); + +class Dir { + constructor(handle, path, options) { + if (handle == null) throw new ERR_MISSING_ARGS('handle'); + this[kDirHandle] = handle; + this[kDirPath] = path; + this[kDirClosed] = false; + + this[kDirOptions] = getOptions(options, { + encoding: 'utf8' + }); + + this[kDirReadPromisified] = internalUtil.promisify(this.read).bind(this); + this[kDirClosePromisified] = internalUtil.promisify(this.close).bind(this); + } + + get path() { + return this[kDirPath]; + } + + read(options, callback) { + if (this[kDirClosed] === true) { + throw new ERR_DIR_CLOSED(); + } + + callback = typeof options === 'function' ? options : callback; + if (callback === undefined) { + return this[kDirReadPromisified](options); + } else if (typeof callback !== 'function') { + throw new ERR_INVALID_CALLBACK(callback); + } + options = getOptions(options, this[kDirOptions]); + + const req = new FSReqCallback(); + req.oncomplete = (err, result) => { + if (err || result === null) { + return callback(err, result); + } + getDirent(this[kDirPath], result[0], result[1], callback); + }; + + this[kDirHandle].read( + options.encoding, + req + ); + } + + readSync(options) { + if (this[kDirClosed] === true) { + throw new ERR_DIR_CLOSED(); + } + + options = getOptions(options, this[kDirOptions]); + + const ctx = { path: this[kDirPath] }; + const result = this[kDirHandle].read( + options.encoding, + undefined, + ctx + ); + handleErrorFromBinding(ctx); + + if (result === null) { + return result; + } + + return getDirent(this[kDirPath], result[0], result[1]); + } + + close(callback) { + if (this[kDirClosed] === true) { + throw new ERR_DIR_CLOSED(); + } + + if (callback === undefined) { + return this[kDirClosePromisified](); + } else if (typeof callback !== 'function') { + throw new ERR_INVALID_CALLBACK(callback); + } + + this[kDirClosed] = true; + const req = new FSReqCallback(); + req.oncomplete = callback; + this[kDirHandle].close(req); + } + + closeSync() { + if (this[kDirClosed] === true) { + throw new ERR_DIR_CLOSED(); + } + + this[kDirClosed] = true; + const ctx = { path: this[kDirPath] }; + const result = this[kDirHandle].close(undefined, ctx); + handleErrorFromBinding(ctx); + return result; + } + + async* entries() { + try { + while (true) { + const result = await this[kDirReadPromisified](); + if (result === null) { + break; + } + yield result; + } + } finally { + await this[kDirClosePromisified](); + } + } +} + +Object.defineProperty(Dir.prototype, Symbol.asyncIterator, { + value: Dir.prototype.entries, + enumerable: false, + writable: true, + configurable: true, +}); + +function opendir(path, options, callback) { + callback = typeof options === 'function' ? options : callback; + if (typeof callback !== 'function') { + throw new ERR_INVALID_CALLBACK(callback); + } + path = getValidatedPath(path); + options = getOptions(options, { + encoding: 'utf8' + }); + + function opendirCallback(error, handle) { + if (error) { + callback(error); + } else { + callback(null, new Dir(handle, path, options)); + } + } + + const req = new FSReqCallback(); + req.oncomplete = opendirCallback; + + dirBinding.opendir( + pathModule.toNamespacedPath(path), + options.encoding, + req + ); +} + +function opendirSync(path, options) { + path = getValidatedPath(path); + options = getOptions(options, { + encoding: 'utf8' + }); + + const ctx = { path }; + const handle = dirBinding.opendir( + pathModule.toNamespacedPath(path), + options.encoding, + undefined, + ctx + ); + handleErrorFromBinding(ctx); + + return new Dir(handle, path, options); +} + +module.exports = { + Dir, + opendir, + opendirSync +}; |