From d0c945fc23ec8840f99588469f3ec031148a09c7 Mon Sep 17 00:00:00 2001 From: exoego Date: Tue, 8 Oct 2019 10:23:25 +0900 Subject: [PATCH] Overhaul fs module * Add more Future-based extension method * More tests * Update Fs.contsntas --- .../io/scalajs/nodejs/fs/FSConstants.scala | 33 ++ .../main/scala/io/scalajs/nodejs/fs/Fs.scala | 53 ++-- .../scala/io/scalajs/nodejs/fs/package.scala | 285 +++++++++++++++--- .../io/scalajs/nodejs/fs/FsAsyncTest.scala | 14 + .../io/scalajs/nodejs/fs/FsAsyncTest.scala | 13 + .../io/scalajs/nodejs/fs/FsAsyncTest.scala | 45 ++- 6 files changed, 364 insertions(+), 79 deletions(-) diff --git a/app/current/src/main/scala/io/scalajs/nodejs/fs/FSConstants.scala b/app/current/src/main/scala/io/scalajs/nodejs/fs/FSConstants.scala index 51f853809..a36b906a9 100644 --- a/app/current/src/main/scala/io/scalajs/nodejs/fs/FSConstants.scala +++ b/app/current/src/main/scala/io/scalajs/nodejs/fs/FSConstants.scala @@ -1,6 +1,8 @@ package io.scalajs.nodejs package fs +import com.thoughtworks.enableIf + import scala.scalajs.js /** @@ -36,6 +38,24 @@ trait FSConstants extends js.Object { */ val X_OK: FileMode = js.native + ///////////////////////////////////////////////////////////////////////////////// + // File Mode Constants + // + // The following constants are meant for use with fs.Stats. + ///////////////////////////////////////////////////////////////////////////////// ` + val S_IRGRP: FileMode = js.native + val S_IROTH: FileMode = js.native + val S_IRUSR: FileMode = js.native + val S_IRWXG: FileMode = js.native + val S_IRWXO: FileMode = js.native + val S_IRWXU: FileMode = js.native + val S_IWGRP: FileMode = js.native + val S_IWOTH: FileMode = js.native + val S_IWUSR: FileMode = js.native + val S_IXGRP: FileMode = js.native + val S_IXOTH: FileMode = js.native + val S_IXUSR: FileMode = js.native + ///////////////////////////////////////////////////////////////////////////////// // File Open Constants // @@ -120,6 +140,11 @@ trait FSConstants extends js.Object { */ val O_NONBLOCK: Int = js.native + /** + * Flag indicating that the file is opened for synchronized I/O with write operations waiting for data integrity. + */ + val O_DSYNC: Int = js.native + ///////////////////////////////////////////////////////////////////////////////// // File Type Constants // @@ -167,4 +192,12 @@ trait FSConstants extends js.Object { */ val S_IFSOCK: FileType = js.native + ///////////////////////////////////////////////////////////////////////////////// + // File Copy Constants + ///////////////////////////////////////////////////////////////////////////////// + val COPYFILE_EXCL: Int = js.native + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + val COPYFILE_FICLONE: Int = js.native + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + val COPYFILE_FICLONE_FORCE: Int = js.native } diff --git a/app/current/src/main/scala/io/scalajs/nodejs/fs/Fs.scala b/app/current/src/main/scala/io/scalajs/nodejs/fs/Fs.scala index f8f246e5c..76e807ff9 100644 --- a/app/current/src/main/scala/io/scalajs/nodejs/fs/Fs.scala +++ b/app/current/src/main/scala/io/scalajs/nodejs/fs/Fs.scala @@ -7,7 +7,7 @@ import io.scalajs.nodejs.events.IEventEmitter import scala.scalajs.js import scala.scalajs.js.annotation.JSImport -import scala.scalajs.js.typedarray.{ArrayBufferView, Uint8Array} +import scala.scalajs.js.typedarray import scala.scalajs.js.| /** @@ -456,10 +456,10 @@ trait Fs extends IEventEmitter with FSConstants { * @param path the path * @param mode the mode */ - def mkdirSync(path: Buffer | String, mode: FileMode = js.native): Unit = js.native + def mkdirSync(path: Path, mode: FileMode = js.native): Unit = js.native @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) - def mkdirSync(path: Buffer | String, mode: MkdirOptions): Unit = js.native + def mkdirSync(path: Path, mode: MkdirOptions): Unit = js.native /** * Creates a unique temporary directory. @@ -729,7 +729,7 @@ trait Fs extends IEventEmitter with FSConstants { * @return the symbolic link's string value. */ def readlinkSync(path: Path, options: String | FileEncodingOptions): Output = js.native - def readlinkSync(path: Path): Output = js.native + def readlinkSync(path: Path): String = js.native /** * Asynchronous realpath(2). @@ -980,7 +980,7 @@ trait Fs extends IEventEmitter with FSConstants { * @param options the [[FSWatcherOptions optional settings]] * @param listener the callback */ - def watchFile(filename: Path, options: FileWatcherOptions, listener: FsCallback2[Stats, Stats]): Unit = + def watchFile(filename: Path, options: FileWatcherOptions, listener: js.Function2[Stats, Stats, Any]): Unit = js.native /** @@ -993,7 +993,7 @@ trait Fs extends IEventEmitter with FSConstants { * @param filename the filename (Buffer | String) * @param listener the callback */ - def watchFile(filename: Buffer | String, listener: FsCallback2[Stats, Stats]): Unit = js.native + def watchFile(filename: Path, listener: js.Function2[Stats, Stats, Any]): Unit = js.native /** * Write buffer to the file specified by fd. @@ -1013,7 +1013,7 @@ trait Fs extends IEventEmitter with FSConstants { * @example {{{ fs.write(fd, buffer[, offset[, length[, position]]], callback) }}} **/ def write(fd: FileDescriptor, - buffer: Uint8Array, + buffer: typedarray.Uint8Array, offset: Int | Null, length: Int | Null, position: Int | Null, @@ -1049,19 +1049,18 @@ trait Fs extends IEventEmitter with FSConstants { string: String, position: Int, encoding: String, - callback: FsCallback2[Int, String]): Unit = js.native - def write(fd: FileDescriptor, string: String, position: Int, callback: FsCallback2[Int, String]): Unit = js.native - def write(fd: FileDescriptor, string: String, encoding: String, callback: FsCallback2[Int, String]): Unit = js.native - def write(fd: FileDescriptor, string: String, callback: FsCallback2[Int, String]): Unit = js.native + callback: FsCallback2[Int, String]): Unit = js.native + def write(fd: FileDescriptor, string: String, position: Int, callback: FsCallback2[Int, String]): Unit = js.native + def write(fd: FileDescriptor, string: String, callback: FsCallback2[Int, String]): Unit = js.native /** * Asynchronously writes data to a file, replacing the file if it already exists. data can be a string or a buffer. * The encoding option is ignored if data is a buffer. It defaults to 'utf8' * @example fs.writeFile(file, data[, options], callback) */ - def writeFile(file: String, data: Uint8Array, options: FileWriteOptions, callback: FsCallback0): Unit = + def writeFile(file: String, data: typedarray.Uint8Array, options: FileWriteOptions, callback: FsCallback0): Unit = js.native - def writeFile(file: String, data: Uint8Array, callback: FsCallback0): Unit = js.native + def writeFile(file: String, data: typedarray.Uint8Array, callback: FsCallback0): Unit = js.native def writeFile(file: String, data: String, options: FileWriteOptions, callback: FsCallback0): Unit = js.native def writeFile(file: String, data: String, callback: FsCallback0): Unit = js.native @@ -1076,7 +1075,9 @@ trait Fs extends IEventEmitter with FSConstants { * @return undefined. * @example fs.writeFileSync(file, data[, options]) */ - def writeFileSync(file: Path | FileDescriptor, data: Uint8Array, options: FileWriteOptions = js.native): Unit = + def writeFileSync(file: Path | FileDescriptor, + data: typedarray.Uint8Array, + options: FileWriteOptions = js.native): Unit = js.native def writeFileSync(file: Path | FileDescriptor, data: String, options: FileWriteOptions): Unit = js.native @@ -1097,10 +1098,14 @@ trait Fs extends IEventEmitter with FSConstants { * @param position refers to the offset from the beginning of the file where this data should be written. * @example {{{ fs.writeSync(fd, buffer[, offset[, length[, position]]]) }}} */ - def writeSync(fd: FileDescriptor, buffer: Uint8Array, offset: Int, length: Int, position: Int = js.native): Unit = + def writeSync(fd: FileDescriptor, + buffer: typedarray.Uint8Array, + offset: Int, + length: Int, + position: Int = js.native): Unit = js.native - def writeSync(fd: FileDescriptor, buffer: Uint8Array, offset: Int): Unit = js.native - def writeSync(fd: FileDescriptor, buffer: Uint8Array): Unit = js.native + def writeSync(fd: FileDescriptor, buffer: typedarray.Uint8Array, offset: Int): Unit = js.native + def writeSync(fd: FileDescriptor, buffer: typedarray.Uint8Array): Unit = js.native def writeSync(fd: FileDescriptor, buffer: BufferLike, offset: Int, length: Int, position: Int): Unit = js.native def writeSync(fd: FileDescriptor, buffer: BufferLike, offset: Int, length: Int): Unit = js.native def writeSync(fd: FileDescriptor, buffer: BufferLike, offset: Int): Unit = js.native @@ -1119,11 +1124,14 @@ trait Fs extends IEventEmitter with FSConstants { def writeSync(fd: FileDescriptor, data: String, encoding: String): Unit = js.native def writeSync(fd: FileDescriptor, data: String): Unit = js.native + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs12) def writev(fd: FileDescriptor, - buffers: js.Array[ArrayBufferView], + buffers: js.Array[typedarray.ArrayBufferView], position: Int, - fsCallback2: FsCallback2[Int, js.Array[ArrayBufferView]]): Unit = js.native - def writevSync(fd: FileDescriptor, buffers: js.Array[ArrayBufferView], position: Int = js.native): Unit = js.native + fsCallback2: FsCallback2[Int, js.Array[typedarray.ArrayBufferView]]): Unit = js.native + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs12) + def writevSync(fd: FileDescriptor, buffers: js.Array[typedarray.ArrayBufferView], position: Int = js.native): Unit = + js.native } /** @@ -1295,6 +1303,7 @@ class RmdirOptions(var emfileWait: js.UndefOr[Int] = 1000, @js.native trait RealpathObject extends js.Object { - def native(path: Path, options: FileEncodingOptions = js.native): Output = js.native - def native(path: Path, encoding: String): String = js.native + def native(path: Path, options: FileEncodingOptions, callback: FsCallback1[Output]): Unit = js.native + def native(path: Path, encoding: String, callback: FsCallback1[Output]): Unit = js.native + def native(path: Path, callback: FsCallback1[String]): Unit = js.native } diff --git a/app/current/src/main/scala/io/scalajs/nodejs/fs/package.scala b/app/current/src/main/scala/io/scalajs/nodejs/fs/package.scala index 926138101..f107ab8e0 100644 --- a/app/current/src/main/scala/io/scalajs/nodejs/fs/package.scala +++ b/app/current/src/main/scala/io/scalajs/nodejs/fs/package.scala @@ -7,17 +7,16 @@ import io.scalajs.util.PromiseHelper._ import scala.concurrent.Future import scala.scalajs.js -import scala.scalajs.js.typedarray.{DataView, TypedArray, Uint8Array} -import scala.scalajs.js.| +import scala.scalajs.js.{typedarray, |} /** * fs package object */ package object fs { - type Path = Uint8Array | String | URL + type Path = typedarray.Uint8Array | String | URL type Time = Int | String | js.Date - type BufferLike = TypedArray[_, _] | DataView + type BufferLike = typedarray.TypedArray[_, _] | typedarray.DataView type Output = String | Buffer type FileWriteOptions = FileAppendOptions @@ -32,6 +31,8 @@ package object fs { // Implicit conversions and classes ///////////////////////////////////////////////////////////////////////////////// + private val isWindows = process.Process.platform == "win32" + /** * File System Extensions * @param instance the given [[Fs file system]] instance @@ -59,87 +60,170 @@ package object fs { } @inline - def chmodFuture(path: Buffer | String, mode: FileMode, callback: js.Function1[FileIOError, Any]): Future[Unit] = { + def chmodFuture(path: Buffer | String, mode: FileMode): Future[Unit] = { promiseWithError0[FileIOError](instance.chmod(path, mode, _)) } @inline - def closeFuture(fd: FileDescriptor): Future[Unit] = promiseWithError0[FileIOError](instance.close(fd, _)) + def chownFuture(path: Path, uid: UID, gid: GID): Future[Unit] = { + promiseWithError0[FileIOError](instance.chown(path, uid, gid, _)) + } + + @inline + def copyFileFuture(src: Path, dest: Path, flags: Flags): Future[Unit] = { + promiseWithError0[FileIOError](instance.copyFile(src, dest, flags, _)) + } + + @inline + def copyFileFuture(src: Path, dest: Path): Future[Unit] = { + promiseWithError0[FileIOError](instance.copyFile(src, dest, _)) + } + + @inline + def existsFuture(path: Path): Future[Boolean] = promiseWithErrorAsBoolean[FileIOError](instance.access(path, _)) + + @inline + def fchmodFuture(fd: FileDescriptor, mode: FileMode): Future[Unit] = { + promiseWithError0[FileIOError](instance.fchmod(fd, mode, _)) + } @inline - def existsFuture(path: String): Future[Boolean] = promiseWithErrorAsBoolean[FileIOError](instance.access(path, _)) + def fchownFuture(fd: FileDescriptor, uid: UID, gid: GID): Future[Unit] = { + promiseWithError0[FileIOError](instance.fchown(fd, uid, gid, _)) + } @inline def fdatasyncFuture(fd: FileDescriptor): Future[Unit] = promiseWithError0[FileIOError](instance.fdatasync(fd, _)) + @inline + def fstatFuture(fd: FileDescriptor, options: StatOptions): Future[Stats] = { + promiseWithError1[FileIOError, Stats](instance.fstat(fd, options, _)) + } + + @inline + def fstatFuture(fd: FileDescriptor): Future[Stats] = { + promiseWithError1[FileIOError, Stats](instance.fstat(fd, _)) + } + + @inline + def fsyncFuture(fd: FileDescriptor): Future[Unit] = { + promiseWithError0[FileIOError](instance.fsync(fd, _)) + } + + @inline + def ftruncateFuture(fd: FileDescriptor): Future[Unit] = { + promiseWithError0[FileIOError](instance.ftruncate(fd, _)) + } + @inline def futimesFuture(fd: FileDescriptor, atime: Time, mtime: Time): Future[Unit] = { promiseWithError0[FileIOError](instance.futimes(fd, atime, mtime, _)) } @inline - def lchmodFuture(path: Buffer | String, mode: FileMode): Future[Unit] = { + def lchmodFuture(path: Path, mode: FileMode): Future[Unit] = { promiseWithError0[FileIOError](instance.lchmod(path, mode, _)) } @inline - def lchownFuture(path: Buffer | String, uid: UID, gid: GID): Future[Unit] = { + def lchownFuture(path: Path, uid: UID, gid: GID): Future[Unit] = { promiseWithError0[FileIOError](instance.lchown(path, uid, gid, _)) } @inline - def linkFuture(srcpath: Buffer | String, dstpath: Buffer | String): Future[Unit] = { + def linkFuture(srcpath: Path, dstpath: Path): Future[Unit] = { promiseWithError0[FileIOError](instance.link(srcpath, dstpath, _)) } @inline - def mkdirFuture(path: Buffer | String, mode: FileMode): Future[Unit] = { + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + def lstatFuture(path: Path, options: StatOptions): Future[Stats] = { + promiseWithError1[FileIOError, Stats](instance.lstat(path, options, _)) + } + + @inline + def lstatFuture(path: Path): Future[Stats] = { + promiseWithError1[FileIOError, Stats](instance.lstat(path, _)) + } + + @inline + def mkdirFuture(path: Path, mode: FileMode): Future[Unit] = { promiseWithError0[FileIOError](instance.mkdir(path, mode, _)) } @inline - def mkdirFuture(path: Buffer | String): Future[Unit] = { + def mkdirFuture(path: Path): Future[Unit] = { promiseWithError0[FileIOError](instance.mkdir(path, _)) } @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) @inline - def mkdirFuture(path: Buffer | String, options: MkdirOptions): Future[Unit] = { + def mkdirFuture(path: Path, options: MkdirOptions): Future[Unit] = { promiseWithError0[FileIOError](instance.mkdir(path, options, _)) } + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def mkdirRecursiveFuture(path: Path): Future[Unit] = { + val recursiveEnabled = new MkdirOptions(recursive = true) + promiseWithError0[FileIOError](instance.mkdir(path, recursiveEnabled, _)) + } + + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def mkdirRecursiveFuture(path: Path, options: MkdirOptions): Future[Unit] = { + val recursiveEnabled = new MkdirOptions( + recursive = true, + mode = options.mode + ) + promiseWithError0[FileIOError](instance.mkdir(path, recursiveEnabled, _)) + } + @inline - def mkdtempFuture(prefix: String, options: FileEncodingOptions = ???): Future[String] = { + def mkdtempFuture(prefix: String, options: FileEncodingOptions): Future[String] = { promiseWithError1[FileIOError, String](instance.mkdtemp(prefix, options, _)) } @inline - def openFuture(path: Buffer | String, flags: Flags, mode: FileMode): Future[FileDescriptor] = { + def mkdtempFuture(prefix: String): Future[String] = { + promiseWithError1[FileIOError, String](instance.mkdtemp(prefix, _)) + } + + @inline + def openFuture(path: Path, flags: Flags, mode: FileMode): Future[FileDescriptor] = { promiseWithError1[FileIOError, FileDescriptor](instance.open(path, flags, mode, _)) } @inline - def openFuture(path: Buffer | String, flags: Flags): Future[FileDescriptor] = { + def openFuture(path: Path, flags: Flags): Future[FileDescriptor] = { promiseWithError1[FileIOError, FileDescriptor](instance.open(path, flags, _)) } @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs12) @inline - def openFuture(path: Buffer | String): Future[FileDescriptor] = { + def openFuture(path: Path): Future[FileDescriptor] = { promiseWithError1[FileIOError, FileDescriptor](instance.open(path, _)) } @inline def readFuture(fd: FileDescriptor, buffer: Buffer, - offset: Int, - length: Int, - position: Int): Future[(Int, Buffer)] = { + offset: Int | Null, + length: Int | Null, + position: Int | Null): Future[(Int, Buffer)] = { promiseWithError2[FileIOError, Int, Buffer](Fs.read(fd, buffer, offset, length, position, _)) } @inline - def readdirFuture(path: Buffer | String, options: String = "utf8"): Future[js.Array[String]] = { + def readdirFuture(path: Path, encoding: String = "utf8"): Future[js.Array[String]] = { + val callback: FsCallback1[js.Array[String]] => Unit = { callback => + instance.readdir(path, encoding, callback.asInstanceOf[FsCallback1[ReaddirArrays]]) + } + promiseWithError1[FileIOError, js.Array[String]](callback) + } + + @inline + def readdirFuture(path: Path, options: FileEncodingOptions): Future[js.Array[String]] = { val callback: FsCallback1[js.Array[String]] => Unit = { callback => instance.readdir(path, options, callback.asInstanceOf[FsCallback1[ReaddirArrays]]) } @@ -147,7 +231,7 @@ package object fs { } @inline - def readdirBufferFuture(path: Buffer | String): Future[js.Array[Buffer]] = { + def readdirBufferFuture(path: Path): Future[js.Array[Buffer]] = { val callback: FsCallback1[js.Array[Buffer]] => Unit = { callback => instance.readdir( path, @@ -172,66 +256,164 @@ package object fs { } @inline - def readFileFuture(file: String, options: ReadFileOptions = null): Future[Output] = { + def readFileFuture(file: Path | FileDescriptor, options: ReadFileOptions): Future[Output] = { promiseWithError1[FileIOError, Output](instance.readFile(file, options, _)) } @inline - def renameFuture(oldPath: String, newPath: String): Future[Unit] = { + def readFileFuture(file: Path | FileDescriptor, encoding: String): Future[String] = { + promiseWithError1[FileIOError, String](instance.readFile(file, encoding, _)) + } + + @inline + def readFileFuture(file: Path | FileDescriptor): Future[Buffer] = { + promiseWithError1[FileIOError, Buffer](instance.readFile(file, _)) + } + + @inline + def readlinkFuture(file: Path, options: String | FileEncodingOptions): Future[Output] = { + promiseWithError1[FileIOError, Output](instance.readlink(file, options, _)) + } + + @inline + def renameFuture(oldPath: Path, newPath: Path): Future[Unit] = { promiseWithError0[FileIOError](instance.rename(oldPath, newPath, _)) } @inline - def realpathFuture(path: String): Future[String] = { - promiseWithError1[FileIOError, String](instance.realpath(path, _)) + def readlinkFuture(file: Path): Future[String] = { + promiseWithError1[FileIOError, String](instance.readlink(file, _)) } @inline - def realpathFuture(path: String, options: FileEncodingOptions): Future[Output] = { - promiseWithError1[FileIOError, Output](instance.realpath(path, options, _)) + def realpathFuture(file: Path, options: String | FileEncodingOptions): Future[Output] = { + promiseWithError1[FileIOError, Output](instance.realpath(file, options, _)) } + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) @inline - def rmdirFuture(path: Buffer | String): Future[Unit] = promiseWithError0[FileIOError](instance.rmdir(path, _)) + def realpathNativeFuture(file: Path, options: FileEncodingOptions): Future[Output] = { + promiseWithError1[FileIOError, Output](instance.realpath.native(file, options, _)) + } + + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def realpathNativeFuture(file: Path, encoding: String): Future[Output] = { + promiseWithError1[FileIOError, Output](instance.realpath.native(file, encoding, _)) + } + + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def realpathNativeFuture(file: Path): Future[String] = { + promiseWithError1[FileIOError, String](instance.realpath.native(file, _)) + } + + @inline + def realpathFuture(file: Path): Future[String] = { + promiseWithError1[FileIOError, String](instance.realpath(file, _)) + } + + @inline + def rmdirFuture(path: Path): Future[Unit] = promiseWithError0[FileIOError](instance.rmdir(path, _)) @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs12) @inline - def rmdirFuture(path: Buffer | String, options: RmdirOptions): Future[Unit] = + def rmdirFuture(path: Path, options: RmdirOptions): Future[Unit] = promiseWithError0[FileIOError](instance.rmdir(path, options, _)) + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs12) @inline - def statFuture(path: String): Future[Stats] = promiseWithError1[FileIOError, Stats](instance.stat(path, _)) + def rmdirRecursiveFuture(path: Path, options: RmdirOptions): Future[Unit] = { + val recursiveEnabled = + new RmdirOptions(recursive = true, maxBusyTries = options.maxBusyTries, emfileWait = options.emfileWait) + promiseWithError0[FileIOError](instance.rmdir(path, recursiveEnabled, _)) + } + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs12) @inline - def symlinkFuture(target: Buffer | String, path: Buffer | String, `type`: String = null): Future[Unit] = { + def rmdirRecursiveFuture(path: Path): Future[Unit] = { + promiseWithError0[FileIOError](instance.rmdir(path, new RmdirOptions(recursive = true), _)) + } + + @inline + def statFuture(path: Path): Future[Stats] = promiseWithError1[FileIOError, Stats](instance.stat(path, _)) + + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def statFuture(path: Path, options: StatOptions): Future[Stats] = { + promiseWithError1[FileIOError, Stats](instance.stat(path, options, _)) + } + + @inline + def symlinkFuture(target: Path, path: Path, `type`: String): Future[Unit] = { promiseWithError0[FileIOError](instance.symlink(target, path, `type`, _)) } @inline - def unlinkFuture(path: Buffer | String): Future[Unit] = promiseWithError0[FileIOError](instance.unlink(path, _)) + def symlinkFuture(target: Path, path: Path): Future[Unit] = { + promiseWithError0[FileIOError](instance.symlink(target, path, _)) + } + + @inline + def truncateFuture(target: Path): Future[Unit] = { + promiseWithError0[FileIOError](instance.truncate(target, _)) + } + + @inline + def truncateFuture(target: Path, length: Int): Future[Unit] = { + promiseWithError0[FileIOError](instance.truncate(target, length, _)) + } + + @inline + def unlinkFuture(path: Path): Future[Unit] = promiseWithError0[FileIOError](instance.unlink(path, _)) @inline - def unwatchFileFuture(filename: Buffer | String): Future[Unit] = + def unwatchFileFuture(filename: Path): Future[Unit] = promiseWithError0[FileIOError](instance.unwatchFile(filename, _)) @inline - def utimesFuture(path: Buffer | String, atime: Int, mtime: Int): Future[Unit] = + def utimesFuture(path: Path, atime: Time, mtime: Time): Future[Unit] = promiseWithError0[FileIOError](instance.utimes(path, atime, mtime, _)) @inline - def watchFuture(filename: String, options: FSWatcherOptions = null): Future[(String, String)] = { - promiseCallback2[String, String](instance.watch(filename, options, _)) + def watchFuture(filename: Path): Future[(EventType, String)] = { + promiseCallback2[EventType, String](instance.watch(filename, _)) + } + + @inline + def watchFuture(filename: Path, options: FSWatcherOptions): Future[(EventType, String)] = { + promiseCallback2[EventType, String](instance.watch(filename, options, _)) + } + + @inline + def watchFileFuture(filename: Path): Future[(Stats, Stats)] = { + promiseCallback2[Stats, Stats](instance.watchFile(filename, _)) + } + + @inline + def watchFileFuture(filename: Path, options: FileWatcherOptions): Future[(Stats, Stats)] = { + promiseCallback2[Stats, Stats](instance.watchFile(filename, options, _)) } @inline def writeFuture(fd: FileDescriptor, - buffer: Uint8Array, + buffer: typedarray.Uint8Array, offset: Int | Null = null, length: Int | Null = null, position: Int | Null = null): Future[(FileType, Buffer)] = { promiseWithError2[FileIOError, Int, Buffer](instance.write(fd, buffer, offset, length, position, _)) } + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def writeFuture(fd: FileDescriptor, + buffer: BufferLike, + offset: Int | Null, + length: Int | Null, + position: Int | Null): Future[(FileType, Buffer)] = { + promiseWithError2[FileIOError, Int, Buffer](instance.write(fd, buffer, offset, length, position, _)) + } + @inline def writeFuture(fd: FileDescriptor, string: String, position: Int, encoding: String): Future[(FileType, String)] = { promiseWithError2[FileIOError, Int, String](instance.write(fd, string, position, encoding, _)) @@ -239,7 +421,7 @@ package object fs { @inline def writeFuture(fd: FileDescriptor, string: String, position: Int): Future[(FileType, String)] = { - promiseWithError2[FileIOError, Int, String](instance.write(fd, string, position, null, _)) + promiseWithError2[FileIOError, Int, String](instance.write(fd, string, position, _)) } @inline @@ -248,7 +430,19 @@ package object fs { } @inline - def writeFileFuture(file: String, data: Buffer, options: FileWriteOptions = null): Future[Unit] = { + def writeFileFuture(file: String, data: typedarray.Uint8Array, options: FileWriteOptions = null): Future[Unit] = { + promiseWithError0[FileIOError](instance.writeFile(file, data, options, _)) + } + + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def writeFileFuture(file: String, data: BufferLike): Future[Unit] = { + promiseWithError0[FileIOError](instance.writeFile(file, data, _)) + } + + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs10) + @inline + def writeFileFuture(file: String, data: BufferLike, options: FileWriteOptions): Future[Unit] = { promiseWithError0[FileIOError](instance.writeFile(file, data, options, _)) } @@ -261,6 +455,15 @@ package object fs { def writeFileFuture(file: String, data: String): Future[Unit] = { promiseWithError0[FileIOError](instance.writeFile(file, data, _)) } - } + @enableIf(io.scalajs.nodejs.internal.CompilerSwitches.gteNodeJs12) + @inline + def writevFuture(fd: FileDescriptor, + buffers: js.Array[typedarray.ArrayBufferView], + position: Int): Future[(Int, js.Array[typedarray.ArrayBufferView])] = { + promiseWithError2[FileIOError, Int, js.Array[typedarray.ArrayBufferView]]( + instance.writev(fd, buffers, position, _) + ) + } + } } diff --git a/app/current/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala b/app/current/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala index e778c2cdf..bcf274ca6 100644 --- a/app/current/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala +++ b/app/current/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala @@ -39,5 +39,19 @@ class FsAsyncTest extends AsyncFunSpec with BeforeAndAfterEach { assert(!dirExistsAfterRmdir) } } + + it("should have alias for recursive-rmdir") { + for { + dirExistsBeforeMkdir <- Fs.existsFuture(dir) + _ <- Fs.mkdirRecursiveFuture(dir) + dirExistsAfterMkdir <- Fs.existsFuture(dir) + _ <- Fs.rmdirRecursiveFuture("x.v12") + dirExistsAfterRmdir <- Fs.existsFuture("x.v12") + } yield { + assert(!dirExistsBeforeMkdir) + assert(dirExistsAfterMkdir) + assert(!dirExistsAfterRmdir) + } + } } } diff --git a/app/nodejs-v10/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala b/app/nodejs-v10/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala index 575a1af94..476ada7b8 100644 --- a/app/nodejs-v10/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala +++ b/app/nodejs-v10/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala @@ -32,5 +32,18 @@ class FsAsyncTest extends AsyncFunSpec with BeforeAndAfterEach { assert(dirExistsAfterMkdir) } } + + it("should have alias for recursive-mkdir") { + for { + dirExistsBeforeMkdir <- Fs.existsFuture(dir) + _ <- Fs.mkdirRecursiveFuture(dir) + dirStat <- Fs.statFuture(dir) + dirExistsAfterMkdir <- Fs.existsFuture(dir) + } yield { + assert(!dirExistsBeforeMkdir) + assert(dirStat.isDirectory()) + assert(dirExistsAfterMkdir) + } + } } } diff --git a/app/nodejs-v8/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala b/app/nodejs-v8/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala index 72cda86b8..72bb66949 100644 --- a/app/nodejs-v8/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala +++ b/app/nodejs-v8/src/test/scala/io/scalajs/nodejs/fs/FsAsyncTest.scala @@ -67,23 +67,36 @@ class FsAsyncTest extends AsyncFunSpec with BeforeAndAfterEach { } } - describe("appendFileFuture") { - it("should support option") { - for { - _ <- Fs.appendFileFuture("x.AppendFile.txt", "yay") - _ <- Fs.appendFileFuture("x.AppendFile.sh", "echo 0", new FileAppendOptions(mode = Fs.constants.X_OK)) - defaultStat <- Fs.statFuture("x.AppendFile.txt") - executableStat <- Fs.statFuture("x.AppendFile.sh") - _ <- Fs.unlinkFuture("x.AppendFile.txt") - _ <- Fs.unlinkFuture("x.AppendFile.sh") - } yield { - assert((defaultStat.mode & Fs.constants.R_OK) > 0) - assert((defaultStat.mode & Fs.constants.X_OK) === 0) - assert((executableStat.mode & Fs.constants.R_OK) === 0) - assert((executableStat.mode & Fs.constants.X_OK) > 0) - succeed - } + it("should support appendFileFuture") { + for { + _ <- Fs.appendFileFuture("x.AppendFile.txt", "yay") + _ <- Fs.appendFileFuture("x.AppendFile.sh", "echo 0", new FileAppendOptions(mode = Fs.constants.X_OK)) + defaultStat <- Fs.statFuture("x.AppendFile.txt") + executableStat <- Fs.statFuture("x.AppendFile.sh") + _ <- Fs.unlinkFuture("x.AppendFile.txt") + _ <- Fs.unlinkFuture("x.AppendFile.sh") + } yield { + assert((defaultStat.mode & Fs.constants.R_OK) > 0) + assert((defaultStat.mode & Fs.constants.X_OK) === 0) + assert((executableStat.mode & Fs.constants.R_OK) === 0) + assert((executableStat.mode & Fs.constants.X_OK) > 0) + succeed } } + + it("should support copyFileFuture") { + for { + _ <- Fs.appendFileFuture("x.CopyFile.txt", "yay") + _ <- Fs.copyFileFuture("x.CopyFile.txt", "x.CopyFile_2.txt") + _ <- Fs.copyFileFuture("x.CopyFile.txt", "x.CopyFile_2.txt") // succeed + _ <- Fs.copyFileFuture("x.CopyFile.txt", "x.CopyFile_2.txt", Fs.constants.COPYFILE_EXCL).failed + stat <- Fs.statFuture("x.CopyFile_2.txt") + _ <- Fs.unlinkFuture("x.CopyFile.txt") + _ <- Fs.unlinkFuture("x.CopyFile_2.txt") + } yield { + assert(stat.isFile()) + } + } + } }