From e48eea5ef4f1ecc44017c11d0250f3d418f3f692 Mon Sep 17 00:00:00 2001 From: Serhii Khoma Date: Sun, 6 Oct 2024 14:58:50 +0700 Subject: [PATCH] feat: move Options to separate file --- src/Node/FS.purs | 41 +- src/Node/FS/Aff.purs | 155 +++---- src/Node/FS/Aff/Dir.purs | 16 +- src/Node/FS/Async.purs | 598 +++++++++++++-------------- src/Node/FS/Constants.purs | 1 + src/Node/FS/Dir.purs | 25 +- src/Node/FS/Internal/AffUtils.purs | 71 ++++ src/Node/FS/Internal/Utils.purs | 42 ++ src/Node/FS/Options.purs | 329 +++++++++++++++ src/Node/FS/Sync.js | 31 +- src/Node/FS/Types.purs | 34 ++ test/Test/Node/FS/OpendirAndDir.purs | 3 +- test/Test/Node/FS/Sync.purs | 3 +- 13 files changed, 859 insertions(+), 490 deletions(-) create mode 100644 src/Node/FS/Internal/AffUtils.purs create mode 100644 src/Node/FS/Internal/Utils.purs create mode 100644 src/Node/FS/Options.purs create mode 100644 src/Node/FS/Types.purs diff --git a/src/Node/FS.purs b/src/Node/FS.purs index 9db38cb..644e7cf 100644 --- a/src/Node/FS.purs +++ b/src/Node/FS.purs @@ -1,45 +1,8 @@ -module Node.FS - ( FileDescriptor - , FileMode - , SymlinkType(..) - , symlinkTypeToNode - , BufferLength - , BufferOffset - , ByteCount - , FilePosition - , module Exports - ) where +module Node.FS (module Exports) where import Prelude import Data.Nullable (Nullable) import Data.Nullable as Nullable import Node.FS.Constants (FileFlags(..), fileFlagsToNode) as Exports - -foreign import data FileDescriptor :: Type - -type FileMode = Int -type FilePosition = Int -type BufferLength = Int -type BufferOffset = Int -type ByteCount = Int - --- | Symlink varieties. -data SymlinkType = FileLink | DirLink | JunctionLink | AutodetectLink - --- | Convert a `SymlinkType` to a `String` in the format expected by the --- | Node.js filesystem API. -symlinkTypeToNode :: SymlinkType -> Nullable String -symlinkTypeToNode ty = case ty of - FileLink -> Nullable.notNull "file" - DirLink -> Nullable.notNull "dir" - JunctionLink -> Nullable.notNull "junction" - AutodetectLink -> Nullable.null - -instance showSymlinkType :: Show SymlinkType where - show FileLink = "FileLink" - show DirLink = "DirLink" - show JunctionLink = "JunctionLink" - show AutodetectLink = "AutodetectLink" - -derive instance eqSymlinkType :: Eq SymlinkType +import Node.FS.Types as Exports diff --git a/src/Node/FS/Aff.purs b/src/Node/FS/Aff.purs index 37ddf91..e1438d0 100644 --- a/src/Node/FS/Aff.purs +++ b/src/Node/FS/Aff.purs @@ -9,8 +9,8 @@ module Node.FS.Aff , truncate , chown , chmod - , stat , lstat + , stat , link , symlink , readlink @@ -40,8 +40,11 @@ module Node.FS.Aff , appendTextFile , fdOpen , fdRead + , fdRead' , fdNext , fdWrite + , fdWrite' + , fdWriteString , fdAppend , fdClose , cp @@ -69,7 +72,6 @@ module Node.FS.Aff -- , watch -- , watchFile , writev - , module Exports ) where import Prelude @@ -78,84 +80,20 @@ import Data.DateTime (DateTime) import Data.Either (Either(..)) import Data.Maybe (Maybe) import Data.Tuple (Tuple) -import Effect (Effect) import Effect.Aff (Aff, Error, makeAff, nonCanceler) import Node.Buffer (Buffer) import Node.Encoding (Encoding) -import Node.FS as F -import Node.FS.Async (CpOptions, CpForce(..), cpOptionsDefault, OpendirOptions , opendirOptionsDefault, RmOptions, rmOptionsDefault, RmdirOptions, rmdirOptionsDefault) as Exports -import Node.FS.Async (CpOptions, OpendirOptions, RmdirOptions, RmOptions) +import Node.FS.Types (BufferLength, BufferOffset, ByteCount, FileDescriptor, FileMode, FilePosition, SymlinkType) +import Node.FS.Internal.AffUtils (toAff1, toAff2, toAff3, toAff4, toAff5) +import Node.FS.Options (CpOptions, FdReadOptions, FdWriteOptions, OpendirOptions, RealpathOptions, RmOptions, RmdirOptions) +import Node.FS.Constants (AccessMode, CopyMode, FileFlags) import Node.FS.Async as A -import Node.FS.Constants (AccessMode, CopyMode) import Node.FS.Dir (Dir) import Node.FS.Dirent (Dirent, DirentNameTypeBuffer, DirentNameTypeString) import Node.FS.Perms (Perms) import Node.FS.Stats (Stats) import Node.Path (FilePath) -toAff - :: forall a - . (A.Callback a -> Effect Unit) - -> Aff a -toAff p = makeAff \k -> p k $> nonCanceler - -toAff1 - :: forall a x - . (x -> A.Callback a -> Effect Unit) - -> x - -> Aff a -toAff1 f a = toAff (f a) - -toAff2 - :: forall a x y - . (x -> y -> A.Callback a -> Effect Unit) - -> x - -> y - -> Aff a -toAff2 f a b = toAff (f a b) - -toAff3 - :: forall a x y z - . (x -> y -> z -> A.Callback a -> Effect Unit) - -> x - -> y - -> z - -> Aff a -toAff3 f a b c = toAff (f a b c) - --- toAff4 --- :: forall a x y z --- . (x -> y -> z -> y -> A.Callback a -> Effect Unit) --- -> x --- -> y --- -> z --- -> y --- -> Aff a --- toAff4 f a b c d = toAff (f a b c d) - -toAff5 - :: forall a w v x y z - . (w -> v -> x -> y -> z -> A.Callback a -> Effect Unit) - -> w - -> v - -> x - -> y - -> z - -> Aff a -toAff5 f a b c d e = toAff (f a b c d e) - --- toAff6 --- :: forall a w v x y z t --- . (w -> v -> x -> y -> z -> t -> A.Callback a -> Effect Unit) --- -> w --- -> v --- -> x --- -> y --- -> z --- -> t --- -> Aff a --- toAff6 f a b c d e t = toAff (f a b c d e t) - access :: FilePath -> Aff (Maybe Error) access path = makeAff \k -> do A.access path (k <<< Right) @@ -226,7 +164,7 @@ link = toAff2 A.link symlink :: FilePath -> FilePath - -> F.SymlinkType + -> SymlinkType -> Aff Unit symlink = toAff3 A.symlink @@ -246,7 +184,7 @@ realpath = toAff1 A.realpath -- | Find the canonicalized absolute location for a path using a cache object -- | for already resolved paths. -- | -realpath' :: forall cache. FilePath -> { | cache } -> Aff FilePath +realpath' :: FilePath -> RealpathOptions -> Aff FilePath realpath' = toAff2 A.realpath' -- | @@ -376,46 +314,71 @@ appendTextFile = toAff3 A.appendTextFile -- | for details. fdOpen :: FilePath - -> F.FileFlags - -> Maybe F.FileMode - -> Aff F.FileDescriptor + -> FileFlags + -> Maybe FileMode + -> Aff FileDescriptor fdOpen = toAff3 A.fdOpen -- | Read from a file asynchronously. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_read_fd_buffer_offset_length_position_callback) -- | for details. fdRead - :: F.FileDescriptor + :: FileDescriptor -> Buffer - -> F.BufferOffset - -> F.BufferLength - -> Maybe F.FilePosition - -> Aff F.ByteCount + -> BufferOffset + -> BufferLength + -> Maybe FilePosition + -> Aff ByteCount fdRead = toAff5 A.fdRead +-- | Read from a file asynchronously. See the [Node Documentation](https://nodejs.org/docs/latest/api/fs.html#fsreadfd-options-callback) +-- | for details. +fdRead' + :: FileDescriptor + -> FdReadOptions + -> Aff (Tuple ByteCount Buffer) +fdRead' = toAff2 A.fdRead' + -- | Convenience function to fill the whole buffer from the current -- | file position. -fdNext :: F.FileDescriptor -> Buffer -> Aff F.ByteCount +fdNext :: FileDescriptor -> Buffer -> Aff ByteCount fdNext = toAff2 A.fdNext -- | Write to a file asynchronously. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_write_fd_buffer_offset_length_position_callback) -- | for details. fdWrite - :: F.FileDescriptor + :: FileDescriptor -> Buffer - -> F.BufferOffset - -> F.BufferLength - -> Maybe F.FilePosition - -> Aff F.ByteCount + -> BufferOffset + -> BufferLength + -> Maybe FilePosition + -> Aff ByteCount fdWrite = toAff5 A.fdWrite +-- | Write from a file asynchronously. See the [Node Documentation](https://nodejs.org/docs/latest/api/fs.html#fswritefd-options-callback) +-- | for details. +fdWrite' + :: FileDescriptor + -> FdWriteOptions + -> Aff (Tuple ByteCount Buffer) +fdWrite' = toAff2 A.fdWrite' + +-- It is unsafe to use fs.write() multiple times on the same file without waiting for the callback. For this scenario, fs.createWriteStream() is recommended. +fdWriteString + :: FileDescriptor + -> String + -> Maybe FilePosition + -> Encoding + -> Aff (Tuple ByteCount String) +fdWriteString = toAff4 A.fdWriteString + -- | Convenience function to append the whole buffer to the current -- | file position. -fdAppend :: F.FileDescriptor -> Buffer -> Aff F.ByteCount +fdAppend :: FileDescriptor -> Buffer -> Aff ByteCount fdAppend = toAff2 A.fdAppend -- | Close a file asynchronously. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_close_fd_callback) -- | for details. -fdClose :: F.FileDescriptor -> Aff Unit +fdClose :: FileDescriptor -> Aff Unit fdClose = toAff1 A.fdClose -- | Copy a file asynchronously. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fspromises_copyfile_src_dest_mode) @@ -428,32 +391,32 @@ cp' = toAff3 A.cp' -- | Change permissions on a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fchmod_fd_mode_callback) -- | for details. -fchmod :: F.FileDescriptor -> Perms -> Aff Unit +fchmod :: FileDescriptor -> Perms -> Aff Unit fchmod = toAff2 A.fchmod -- | Change ownership of a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fchown_fd_uid_gid_callback) -- | for details. -fchown :: F.FileDescriptor -> Int -> Int -> Aff Unit +fchown :: FileDescriptor -> Int -> Int -> Aff Unit fchown = toAff3 A.fchown -- | Synchronize a file's in-core state with storage. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fdatasync_fd_callback) -- | for details. -fdatasync :: F.FileDescriptor -> Aff Unit +fdatasync :: FileDescriptor -> Aff Unit fdatasync = toAff1 A.fdatasync -- | Get file status information. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fstat_fd_callback) -- | for details. -fstat :: F.FileDescriptor -> Aff Stats +fstat :: FileDescriptor -> Aff Stats fstat = toAff1 A.fstat -- | Flushes a file descriptor to disk. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fsync_fd_callback) -- | for details. -fsync :: F.FileDescriptor -> Aff Unit +fsync :: FileDescriptor -> Aff Unit fsync = toAff1 A.fsync -- | Truncate a file to a specified length. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_ftruncate_fd_len_callback) -- | for details. -ftruncate :: F.FileDescriptor -> Int -> Aff Unit +ftruncate :: FileDescriptor -> Int -> Aff Unit ftruncate = toAff2 A.ftruncate -- | Change file timestamps for a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_futimes_fd_atime_mtime_callback) @@ -507,7 +470,7 @@ opendir' = toAff2 A.opendir' -- | Read from a file descriptor into a buffer array. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_readv_fd_buffers_position_callback) -- | for details. -readv :: F.FileDescriptor -> Array Buffer -> Maybe F.FilePosition -> Aff (Tuple F.ByteCount (Array Buffer)) +readv :: FileDescriptor -> Array Buffer -> Maybe FilePosition -> Aff (Tuple ByteCount (Array Buffer)) readv = toAff3 A.readv -- | TODO: bigint, path Buffer Url @@ -534,5 +497,5 @@ statfs = toAff1 A.statfs -- | Write from an array of buffers to a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_writev_fd_buffers_position_callback) -- | for details. -writev :: F.FileDescriptor -> Array Buffer -> Maybe F.FilePosition -> Aff (Tuple F.ByteCount (Array Buffer)) +writev :: FileDescriptor -> Array Buffer -> Maybe FilePosition -> Aff (Tuple ByteCount (Array Buffer)) writev = toAff3 A.writev diff --git a/src/Node/FS/Aff/Dir.purs b/src/Node/FS/Aff/Dir.purs index f72ef2b..2fa6fb0 100644 --- a/src/Node/FS/Aff/Dir.purs +++ b/src/Node/FS/Aff/Dir.purs @@ -16,22 +16,10 @@ import Effect.Class (liftEffect) import Effect.Exception (Error) import Effect.Ref (Ref) import Effect.Ref as Ref -import Node.FS.Dir (Callback2, Dir) +import Node.FS.Dir (Dir) import Node.FS.Dir as Dir import Node.FS.Dirent (Dirent, DirentNameTypeString) - -toAff - :: forall a - . (Callback2 a -> Effect Unit) - -> Aff a -toAff p = makeAff \k -> p k $> nonCanceler - -toAff1 - :: forall a x - . (x -> Callback2 a -> Effect Unit) - -> x - -> Aff a -toAff1 f a = toAff (f a) +import Node.FS.Internal.AffUtils read :: Dir -> Aff (Maybe (Dirent DirentNameTypeString)) read = toAff1 Dir.read diff --git a/src/Node/FS/Async.purs b/src/Node/FS/Async.purs index b02a76e..0beecca 100644 --- a/src/Node/FS/Async.purs +++ b/src/Node/FS/Async.purs @@ -1,6 +1,5 @@ module Node.FS.Async - ( Callback - , access + ( access , access' , copyFile , copyFile' @@ -20,12 +19,8 @@ module Node.FS.Async , unlink , rmdir , rmdir' - , rmdirOptionsDefault - , RmdirOptions , rm , rm' - , rmOptionsDefault - , RmOptions , mkdir , mkdir' , readdir @@ -45,15 +40,15 @@ module Node.FS.Async , appendTextFile , fdOpen , fdRead + , fdRead' , fdNext , fdWrite + , fdWrite' + , fdWriteString , fdAppend , fdClose , cp , cp' - , cpOptionsDefault - , CpOptions - , CpForce(..) , fchmod , fchown , fdatasync @@ -71,8 +66,6 @@ module Node.FS.Async -- , openAsBlob , opendir , opendir' - , OpendirOptions - , opendirOptionsDefault , readv , statfs -- , unwatchFile @@ -84,227 +77,206 @@ module Node.FS.Async import Prelude import Data.DateTime (DateTime) -import Data.DateTime.Instant (fromDateTime, unInstant) -import Data.Either (Either(..)) -import Data.Function.Uncurried (Fn2, mkFn2) -import Data.Int (round) import Data.Maybe (Maybe(..)) import Data.Nullable (Nullable, toMaybe, toNullable) -import Data.Time.Duration (Milliseconds(..)) -import Data.Tuple (Tuple(..)) +import Data.Tuple (Tuple) import Effect (Effect) import Effect.Exception (Error) -import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, EffectFn4, EffectFn6, mkEffectFn1, mkEffectFn2, mkEffectFn3, runEffectFn2, runEffectFn3, runEffectFn4, runEffectFn6) +import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, EffectFn4, EffectFn5, EffectFn6, mkEffectFn1, runEffectFn2, runEffectFn3, runEffectFn4, runEffectFn5, runEffectFn6) import Node.Buffer (Buffer, size) import Node.Encoding (Encoding(..), encodingToNode) import Node.FS (FileDescriptor, ByteCount, FilePosition, BufferLength, BufferOffset, FileMode, SymlinkType, symlinkTypeToNode) -import Node.FS.Constants (FileFlags, fileFlagsToNode, AccessMode, CopyMode, defaultAccessMode, defaultCopyMode) +import Node.FS.Constants +import Node.FS.Internal.Utils +import Node.FS.Options import Node.FS.Dir (Dir) import Node.FS.Dirent (Dirent, DirentNameTypeBuffer, DirentNameTypeString) -import Node.FS.Perms (Perms, permsToString, all, mkPerms) +import Node.FS.Perms (Perms, permsToString) import Node.FS.Stats (Stats) import Node.Path (FilePath) +import Unsafe.Coerce (unsafeCoerce) -datetimeToUnixEpochTimeInSeconds :: DateTime -> Int -datetimeToUnixEpochTimeInSeconds date = ms (toEpochMilliseconds date) / 1000 - where - ms (Milliseconds n) = round n - toEpochMilliseconds = unInstant <<< fromDateTime - -type JSCallback a = EffectFn2 (Nullable Error) a Unit - -type JSCallback2 a b = EffectFn3 (Nullable Error) a b Unit - -handleCallback :: forall a. Callback a -> JSCallback a -handleCallback cb = mkEffectFn2 \err a -> case toMaybe err of - Nothing -> cb (Right a) - Just err' -> cb (Left err') - -handleCallback2 :: forall a b. Callback (Tuple a b) -> JSCallback2 a b -handleCallback2 cb = mkEffectFn3 \err a b -> case toMaybe err of - Nothing -> cb (Right (Tuple a b)) - Just err' -> cb (Left err') - --- | Type synonym for callback functions. -type Callback a = Either Error a -> Effect Unit +foreign import accessImpl :: EffectFn3 FilePath AccessMode (EffectFn1 (Nullable Error) Unit) Unit +foreign import copyFileImpl :: EffectFn4 FilePath FilePath CopyMode (JSCallback1 Unit) Unit +foreign import mkdtempImpl :: EffectFn3 FilePath FilePath (JSCallback1 FilePath) Unit +foreign import renameImpl :: EffectFn3 FilePath FilePath (JSCallback1 Unit) Unit +foreign import truncateImpl :: EffectFn3 FilePath Int (JSCallback1 Unit) Unit +foreign import chownImpl :: EffectFn4 FilePath Int Int (JSCallback1 Unit) Unit +foreign import chmodImpl :: EffectFn3 FilePath String (JSCallback1 Unit) Unit +foreign import statImpl :: EffectFn2 FilePath (JSCallback1 Stats) Unit +foreign import lstatImpl :: EffectFn2 FilePath (JSCallback1 Stats) Unit +foreign import linkImpl :: EffectFn3 FilePath FilePath (JSCallback1 Unit) Unit +foreign import symlinkImpl :: EffectFn4 FilePath FilePath (Nullable String) (JSCallback1 Unit) Unit +foreign import readlinkImpl :: EffectFn2 FilePath (JSCallback1 FilePath) Unit +foreign import realpathImpl :: EffectFn3 FilePath RealpathOptionsInternal (JSCallback1 FilePath) Unit +foreign import unlinkImpl :: EffectFn2 FilePath (JSCallback1 Unit) Unit +foreign import rmdirImpl :: EffectFn3 FilePath RmdirOptions (JSCallback1 Unit) Unit +foreign import rmImpl :: EffectFn3 FilePath RmOptions (JSCallback1 Unit) Unit +foreign import mkdirImpl :: EffectFn3 FilePath MkdirOptionsInternal (JSCallback1 Unit) Unit +-- if { withFileTypes: false, recursive: false } => ['Tidy'] +-- if { withFileTypes: false, recursive: true } => [ 'Tidy', 'Tidy/Codegen', 'Tidy/Codegen.purs', 'Tidy/Codegen/Class.purs', .. ] +foreign import readdirImpl :: forall filepathOrDirentOrBuffer . EffectFn3 FilePath ReaddirOptionsInternal (JSCallback1 (Array filepathOrDirentOrBuffer)) Unit +foreign import utimesImpl :: EffectFn4 FilePath Int Int (JSCallback1 Unit) Unit +foreign import readFileImpl :: forall stringOrBuffer . EffectFn3 FilePath ReadFileOptionsInternal (JSCallback1 stringOrBuffer) Unit +foreign import writeFileImpl :: forall stringOrBuffer . EffectFn4 FilePath stringOrBuffer WriteFileOptionsInternal (JSCallback1 Unit) Unit +foreign import appendFileImpl :: forall stringOrBuffer . EffectFn4 FilePath stringOrBuffer AppendFileOptionsInternal (JSCallback1 Unit) Unit +foreign import openImpl :: EffectFn4 FilePath String (Nullable FileMode) (JSCallback1 FileDescriptor) Unit +foreign import readImpl :: EffectFn6 FileDescriptor Buffer BufferOffset BufferLength (Nullable FilePosition) (JSCallback1 ByteCount) Unit + +-- https://nodejs.org/docs/latest/api/fs.html#fsreadfd-options-callback +readWithOptionsImpl :: EffectFn3 FileDescriptor FdReadOptionsInternal (JSCallback2 ByteCount Buffer) Unit +readWithOptionsImpl = unsafeCoerce readImpl + +foreign import writeImpl :: EffectFn6 FileDescriptor Buffer BufferOffset BufferLength (Nullable FilePosition) (JSCallback1 ByteCount) Unit + +-- https://nodejs.org/docs/latest/api/fs.html#fsreadfd-options-callback +writeWithOptionsImpl :: EffectFn3 FileDescriptor FdWriteOptionsInternal (JSCallback2 ByteCount Buffer) Unit +writeWithOptionsImpl = unsafeCoerce writeImpl + +-- https://nodejs.org/docs/latest/api/fs.html#fsreadfd-options-callback +writeStringImpl :: EffectFn5 FileDescriptor String (Nullable FilePosition) String (JSCallback2 ByteCount String) Unit +writeStringImpl = unsafeCoerce writeImpl + +foreign import closeImpl :: EffectFn2 FileDescriptor (JSCallback1 Unit) Unit +foreign import cpImpl :: EffectFn4 FilePath FilePath CpOptionsInternal (JSCallback1 Unit) Unit +foreign import fchmodImpl :: EffectFn3 FileDescriptor String (JSCallback1 Unit) Unit +foreign import fchownImpl :: EffectFn4 FileDescriptor Int Int (JSCallback1 Unit) Unit +foreign import fdatasyncImpl :: EffectFn2 FileDescriptor (JSCallback1 Unit) Unit +foreign import fstatImpl :: EffectFn2 FileDescriptor (JSCallback1 Stats) Unit +foreign import fsyncImpl :: EffectFn2 FileDescriptor (JSCallback1 Unit) Unit +foreign import ftruncateImpl :: EffectFn3 FileDescriptor Int (JSCallback1 Unit) Unit +foreign import futimesImpl :: EffectFn4 FileDescriptor Int Int (JSCallback1 Unit) Unit +foreign import globImpl :: forall filepathOrDirent. EffectFn3 (Array FilePath) (GlobOptionsInternal filepathOrDirent) (JSCallback1 (Array filepathOrDirent)) Unit +foreign import lchmodImpl :: EffectFn3 FilePath String (JSCallback1 Unit) Unit +foreign import lchownImpl :: EffectFn4 FilePath Int Int (JSCallback1 Unit) Unit +foreign import lutimesImpl :: EffectFn4 FilePath Int Int (JSCallback1 Unit) Unit +-- foreign import openAsBlobImpl :: EffectFn2 FilePath (Promise Blob) Unit +foreign import opendirImpl :: EffectFn3 FilePath OpendirOptionsInternal (JSCallback1 Dir) Unit +foreign import readvImpl :: EffectFn4 FileDescriptor (Array Buffer) (Nullable FilePosition) (JSCallback2 ByteCount (Array Buffer)) Unit +foreign import statfsImpl :: EffectFn2 FilePath (JSCallback1 Stats) Unit +-- foreign import unwatchFileImpl :: EffectFn1 FilePath Unit +-- foreign import watchImpl :: EffectFn2 FilePath (EffectFn1 String Unit) Unit +-- foreign import watchFileImpl :: EffectFn2 FilePath (EffectFn2 Stats Stats Unit) Unit +foreign import writevImpl :: EffectFn4 FileDescriptor (Array Buffer) (Nullable FilePosition) (JSCallback2 ByteCount (Array Buffer)) Unit access :: FilePath -> (Maybe Error -> Effect Unit) -> Effect Unit access path = access' path defaultAccessMode access' :: FilePath -> AccessMode -> (Maybe Error -> Effect Unit) -> Effect Unit -access' path mode cb = runEffectFn3 accessImpl path mode $ mkEffectFn1 \err -> do - cb $ toMaybe err +access' path mode cb = runEffectFn3 accessImpl path mode $ mkEffectFn1 (cb <<< toMaybe) -foreign import accessImpl :: EffectFn3 FilePath AccessMode (EffectFn1 (Nullable Error) Unit) Unit - -copyFile :: FilePath -> FilePath -> Callback Unit -> Effect Unit +copyFile :: FilePath -> FilePath -> Callback1 Unit -> Effect Unit copyFile src dest = copyFile' src dest defaultCopyMode -copyFile' :: FilePath -> FilePath -> CopyMode -> Callback Unit -> Effect Unit -copyFile' src dest mode cb = runEffectFn4 copyFileImpl src dest mode (handleCallback cb) - -foreign import copyFileImpl :: EffectFn4 FilePath FilePath CopyMode (JSCallback Unit) Unit +copyFile' :: FilePath -> FilePath -> CopyMode -> Callback1 Unit -> Effect Unit +copyFile' src dest mode cb = runEffectFn4 copyFileImpl src dest mode (handleCallback1 cb) -mkdtemp :: FilePath -> Callback FilePath -> Effect Unit +mkdtemp :: FilePath -> Callback1 FilePath -> Effect Unit mkdtemp prefix = mkdtemp' prefix UTF8 -mkdtemp' :: FilePath -> Encoding -> Callback FilePath -> Effect Unit -mkdtemp' prefix encoding cb = runEffectFn3 mkdtempImpl prefix (encodingToNode encoding) (handleCallback cb) - -foreign import mkdtempImpl :: EffectFn3 FilePath FilePath (JSCallback FilePath) Unit - -foreign import renameImpl :: EffectFn3 FilePath FilePath (JSCallback Unit) Unit -foreign import truncateImpl :: EffectFn3 FilePath Int (JSCallback Unit) Unit -foreign import chownImpl :: EffectFn4 FilePath Int Int (JSCallback Unit) Unit -foreign import chmodImpl :: EffectFn3 FilePath String (JSCallback Unit) Unit -foreign import statImpl :: EffectFn2 FilePath (JSCallback Stats) Unit -foreign import lstatImpl :: EffectFn2 FilePath (JSCallback Stats) Unit -foreign import linkImpl :: EffectFn3 FilePath FilePath (JSCallback Unit) Unit -foreign import symlinkImpl :: EffectFn4 FilePath FilePath (Nullable String) (JSCallback Unit) Unit -foreign import readlinkImpl :: EffectFn2 FilePath (JSCallback FilePath) Unit -foreign import realpathImpl :: forall cache. EffectFn3 FilePath { | cache } (JSCallback FilePath) Unit -foreign import unlinkImpl :: EffectFn2 FilePath (JSCallback Unit) Unit -foreign import rmdirImpl :: EffectFn3 FilePath RmdirOptions (JSCallback Unit) Unit -foreign import rmImpl :: EffectFn3 FilePath RmOptions (JSCallback Unit) Unit -foreign import mkdirImpl :: EffectFn3 FilePath { recursive :: Boolean, mode :: String } (JSCallback Unit) Unit --- if { withFileTypes: false, recursive: false } => ['Tidy'] --- if { withFileTypes: false, recursive: true } => [ 'Tidy', 'Tidy/Codegen', 'Tidy/Codegen.purs', 'Tidy/Codegen/Class.purs', .. ] -foreign import readdirImpl :: forall filepathOrDirentOrBuffer stringOrBuffer. EffectFn3 FilePath { encoding :: stringOrBuffer, recursive :: Boolean, withFileTypes :: Boolean } (JSCallback (Array filepathOrDirentOrBuffer)) Unit -foreign import utimesImpl :: EffectFn4 FilePath Int Int (JSCallback Unit) Unit -foreign import readFileImpl :: forall a opts. EffectFn3 FilePath { | opts } (JSCallback a) Unit -foreign import writeFileImpl :: forall a opts. EffectFn4 FilePath a { | opts } (JSCallback Unit) Unit -foreign import appendFileImpl :: forall a opts. EffectFn4 FilePath a { | opts } (JSCallback Unit) Unit -foreign import openImpl :: EffectFn4 FilePath String (Nullable FileMode) (JSCallback FileDescriptor) Unit -foreign import readImpl :: EffectFn6 FileDescriptor Buffer BufferOffset BufferLength (Nullable FilePosition) (JSCallback ByteCount) Unit -foreign import writeImpl :: EffectFn6 FileDescriptor Buffer BufferOffset BufferLength (Nullable FilePosition) (JSCallback ByteCount) Unit -foreign import closeImpl :: EffectFn2 FileDescriptor (JSCallback Unit) Unit -foreign import cpImpl :: EffectFn4 FilePath FilePath CpOptionsInternal (JSCallback Unit) Unit -foreign import fchmodImpl :: EffectFn3 FileDescriptor String (JSCallback Unit) Unit -foreign import fchownImpl :: EffectFn4 FileDescriptor Int Int (JSCallback Unit) Unit -foreign import fdatasyncImpl :: EffectFn2 FileDescriptor (JSCallback Unit) Unit -foreign import fstatImpl :: EffectFn2 FileDescriptor (JSCallback Stats) Unit -foreign import fsyncImpl :: EffectFn2 FileDescriptor (JSCallback Unit) Unit -foreign import ftruncateImpl :: EffectFn3 FileDescriptor Int (JSCallback Unit) Unit -foreign import futimesImpl :: EffectFn4 FileDescriptor Int Int (JSCallback Unit) Unit -foreign import globImpl :: forall filepathOrDirent. EffectFn3 (Array FilePath) { cwd :: Nullable FilePath, exclude :: Nullable (filepathOrDirent -> Boolean), withFileTypes :: Boolean } (JSCallback (Array filepathOrDirent)) Unit -foreign import lchmodImpl :: EffectFn3 FilePath String (JSCallback Unit) Unit -foreign import lchownImpl :: EffectFn4 FilePath Int Int (JSCallback Unit) Unit -foreign import lutimesImpl :: EffectFn4 FilePath Int Int (JSCallback Unit) Unit --- foreign import openAsBlobImpl :: EffectFn2 FilePath (Promise Blob) Unit -foreign import opendirImpl :: EffectFn3 FilePath { bufferSize :: Int, recursive :: Boolean, encoding :: String } (JSCallback Dir) Unit -foreign import readvImpl :: EffectFn4 FileDescriptor (Array Buffer) (Nullable FilePosition) (JSCallback2 ByteCount (Array Buffer)) Unit -foreign import statfsImpl :: EffectFn2 FilePath (JSCallback Stats) Unit --- foreign import unwatchFileImpl :: EffectFn1 FilePath Unit --- foreign import watchImpl :: EffectFn2 FilePath (EffectFn1 String Unit) Unit --- foreign import watchFileImpl :: EffectFn2 FilePath (EffectFn2 Stats Stats Unit) Unit -foreign import writevImpl :: EffectFn4 FileDescriptor (Array Buffer) (Nullable FilePosition) (JSCallback2 ByteCount (Array Buffer)) Unit +mkdtemp' :: FilePath -> Encoding -> Callback1 FilePath -> Effect Unit +mkdtemp' prefix encoding cb = runEffectFn3 mkdtempImpl prefix (encodingToNode encoding) (handleCallback1 cb) -- | Renames a file. rename :: FilePath -> FilePath - -> Callback Unit + -> Callback1 Unit -> Effect Unit -rename oldFile newFile cb = runEffectFn3 renameImpl oldFile newFile (handleCallback cb) +rename oldFile newFile cb = runEffectFn3 renameImpl oldFile newFile (handleCallback1 cb) -- | Truncates a file to the specified length. truncate :: FilePath -> Int - -> Callback Unit + -> Callback1 Unit -> Effect Unit -truncate file len cb = runEffectFn3 truncateImpl file len (handleCallback cb) +truncate file len cb = runEffectFn3 truncateImpl file len (handleCallback1 cb) -- | Changes the ownership of a file. chown :: FilePath -> Int -> Int - -> Callback Unit + -> Callback1 Unit -> Effect Unit -chown file uid gid cb = runEffectFn4 chownImpl file uid gid (handleCallback cb) +chown file uid gid cb = runEffectFn4 chownImpl file uid gid (handleCallback1 cb) -- | Changes the permissions of a file. chmod :: FilePath -> Perms - -> Callback Unit + -> Callback1 Unit -> Effect Unit -chmod file perms cb = runEffectFn3 chmodImpl file (permsToString perms) (handleCallback cb) +chmod file perms cb = runEffectFn3 chmodImpl file (permsToString perms) (handleCallback1 cb) -- | Gets file statistics. stat :: FilePath - -> Callback Stats + -> Callback1 Stats -> Effect Unit -stat file cb = runEffectFn2 statImpl file (handleCallback $ cb) +stat file cb = runEffectFn2 statImpl file (handleCallback1 $ cb) -- | Gets file or symlink statistics. `lstat` is identical to `stat`, except -- | that if theĀ `FilePath` is a symbolic link, then the link itself is stat-ed, -- | not the file that it refers to. lstat :: FilePath - -> Callback Stats + -> Callback1 Stats -> Effect Unit -lstat file cb = runEffectFn2 lstatImpl file (handleCallback $ cb) +lstat file cb = runEffectFn2 lstatImpl file (handleCallback1 $ cb) -- | Creates a link to an existing file. link :: FilePath -> FilePath - -> Callback Unit + -> Callback1 Unit -> Effect Unit -link src dst cb = runEffectFn3 linkImpl src dst (handleCallback cb) +link src dst cb = runEffectFn3 linkImpl src dst (handleCallback1 cb) -- | Creates a symlink. symlink :: FilePath -> FilePath -> SymlinkType - -> Callback Unit + -> Callback1 Unit -> Effect Unit -symlink src dest ty cb = runEffectFn4 symlinkImpl src dest (symlinkTypeToNode ty) (handleCallback cb) +symlink src dest ty cb = runEffectFn4 symlinkImpl src dest (symlinkTypeToNode ty) (handleCallback1 cb) -- | Reads the value of a symlink. readlink :: FilePath - -> Callback FilePath + -> Callback1 FilePath -> Effect Unit -readlink path cb = runEffectFn2 readlinkImpl path (handleCallback cb) +readlink path cb = runEffectFn2 readlinkImpl path (handleCallback1 cb) -- | Find the canonicalized absolute location for a path. realpath :: FilePath - -> Callback FilePath + -> Callback1 FilePath -> Effect Unit -realpath path cb = runEffectFn3 realpathImpl path {} (handleCallback cb) +realpath path = realpath' path realpathOptionsDefault -- | Find the canonicalized absolute location for a path using a cache object -- | for already resolved paths. realpath' - :: forall cache - . FilePath - -> { | cache } - -> Callback FilePath + :: FilePath + -> RealpathOptions + -> Callback1 FilePath -> Effect Unit -realpath' path cache cb = runEffectFn3 realpathImpl path cache (handleCallback cb) +realpath' path options cb = runEffectFn3 realpathImpl path (realpathOptionsToInternal options) (handleCallback1 cb) -- | Deletes a file. unlink :: FilePath - -> Callback Unit + -> Callback1 Unit -> Effect Unit -unlink file cb = runEffectFn2 unlinkImpl file (handleCallback cb) - -type RmdirOptions = { maxRetries :: Int, retryDelay :: Int } - -rmdirOptionsDefault :: RmdirOptions -rmdirOptionsDefault = { maxRetries: 0, retryDelay: 100 } +unlink file cb = runEffectFn2 unlinkImpl file (handleCallback1 cb) -- | Deletes a directory. rmdir :: FilePath - -> Callback Unit + -> Callback1 Unit -> Effect Unit rmdir path cb = rmdir' path rmdirOptionsDefault cb @@ -312,19 +284,14 @@ rmdir path cb = rmdir' path rmdirOptionsDefault cb rmdir' :: FilePath -> RmdirOptions - -> Callback Unit + -> Callback1 Unit -> Effect Unit -rmdir' path opts cb = runEffectFn3 rmdirImpl path opts (handleCallback cb) - -type RmOptions = { force :: Boolean, maxRetries :: Int, recursive :: Boolean, retryDelay :: Int } - -rmOptionsDefault :: RmOptions -rmOptionsDefault = { force: false, maxRetries: 100, recursive: false, retryDelay: 1000 } +rmdir' path opts cb = runEffectFn3 rmdirImpl path opts (handleCallback1 cb) -- | Deletes a file or directory. rm :: FilePath - -> Callback Unit + -> Callback1 Unit -> Effect Unit rm path = rm' path rmOptionsDefault @@ -332,152 +299,198 @@ rm path = rm' path rmOptionsDefault rm' :: FilePath -> RmOptions - -> Callback Unit + -> Callback1 Unit -> Effect Unit -rm' path opts cb = runEffectFn3 rmImpl path opts (handleCallback cb) +rm' path opts cb = runEffectFn3 rmImpl path opts (handleCallback1 cb) -- | Makes a new directory. mkdir :: FilePath - -> Callback Unit + -> Callback1 Unit -> Effect Unit -mkdir path = mkdir' path { recursive: false, mode: mkPerms all all all } +mkdir path = mkdir' path mkdirOptionsDefault -- | Makes a new directory with the specified permissions. mkdir' :: FilePath - -> { recursive :: Boolean, mode :: Perms } - -> Callback Unit + -> MkdirOptions + -> Callback1 Unit -> Effect Unit -mkdir' file { recursive, mode: perms } cb = runEffectFn3 mkdirImpl file { recursive, mode: permsToString perms } (handleCallback cb) +mkdir' file opts cb = runEffectFn3 mkdirImpl file (mkdirOptionsToInternal opts) (handleCallback1 cb) -- | Reads the contents of a directory. readdir :: FilePath - -> Callback (Array FilePath) + -> Callback1 (Array FilePath) -> Effect Unit -readdir file = readdir' file { recursive: false, encoding: UTF8 } +readdir file = readdir' file readdirFilePathOptionsDefault -- | Reads the contents of a directory. readdir' :: FilePath - -> { recursive :: Boolean, encoding :: Encoding } - -> Callback (Array FilePath) + -> ReaddirFilePathOptions + -> Callback1 (Array FilePath) -> Effect Unit -readdir' file { recursive, encoding } cb = runEffectFn3 readdirImpl file { recursive, encoding: encodingToNode encoding, withFileTypes: false } (handleCallback cb) +readdir' file options cb = runEffectFn3 readdirImpl file (readdirFilePathOptionsToInternal options) (handleCallback1 cb) -- | Reads the contents of a directory. readdirBuffer :: FilePath - -> Callback (Array Buffer) + -> Callback1 (Array Buffer) -> Effect Unit -readdirBuffer file = readdirBuffer' file { recursive: false } +readdirBuffer file = readdirBuffer' file readdirBufferOptionsDefault -- | Reads the contents of a directory. readdirBuffer' :: FilePath - -> { recursive :: Boolean } - -> Callback (Array Buffer) + -> ReaddirBufferOptions + -> Callback1 (Array Buffer) -> Effect Unit -readdirBuffer' file { recursive } cb = runEffectFn3 readdirImpl file { recursive, encoding: "buffer", withFileTypes: false } (handleCallback cb) +readdirBuffer' file options cb = runEffectFn3 readdirImpl file (readdirBufferOptionsToInternal options) (handleCallback1 cb) -- | Reads the contents of a directory. readdirDirent :: FilePath - -> Callback (Array (Dirent DirentNameTypeString)) + -> Callback1 (Array (Dirent DirentNameTypeString)) -> Effect Unit -readdirDirent file = readdirDirent' file { recursive: false, encoding: UTF8 } +readdirDirent file = readdirDirent' file readdirDirentOptionsDefault -- | Reads the contents of a directory. readdirDirent' :: FilePath - -> { recursive :: Boolean, encoding :: Encoding } - -> Callback (Array (Dirent DirentNameTypeString)) + -> ReaddirDirentOptions + -> Callback1 (Array (Dirent DirentNameTypeString)) -> Effect Unit -readdirDirent' file { recursive, encoding } cb = runEffectFn3 readdirImpl file { recursive, encoding: encodingToNode encoding, withFileTypes: true } (handleCallback cb) +readdirDirent' file options cb = runEffectFn3 readdirImpl file (readdirDirentOptionsToInternal options) (handleCallback1 cb) -- | Reads the contents of a directory. readdirDirentBuffer :: FilePath - -> Callback (Array (Dirent DirentNameTypeBuffer)) + -> Callback1 (Array (Dirent DirentNameTypeBuffer)) -> Effect Unit -readdirDirentBuffer file = readdirDirentBuffer' file { recursive: false } +readdirDirentBuffer file = readdirDirentBuffer' file readdirDirentBufferOptionsDefault -- | Reads the contents of a directory. readdirDirentBuffer' :: FilePath - -> { recursive :: Boolean } - -> Callback (Array (Dirent DirentNameTypeBuffer)) + -> ReaddirDirentBufferOptions + -> Callback1 (Array (Dirent DirentNameTypeBuffer)) -> Effect Unit -readdirDirentBuffer' file { recursive } cb = runEffectFn3 readdirImpl file { recursive, encoding: "buffer", withFileTypes: true } (handleCallback cb) +readdirDirentBuffer' file options cb = runEffectFn3 readdirImpl file (readdirDirentBufferOptionsToInternal options) (handleCallback1 cb) -- | Sets the accessed and modified times for the specified file. utimes :: FilePath -> DateTime -> DateTime - -> Callback Unit - -> Effect Unit -utimes file atime mtime cb = runEffectFn4 utimesImpl file (datetimeToUnixEpochTimeInSeconds atime) (datetimeToUnixEpochTimeInSeconds mtime) (handleCallback cb) - --- | Reads the entire contents of a file returning the result as a raw buffer. -readFile - :: FilePath - -> Callback Buffer + -> Callback1 Unit -> Effect Unit -readFile file cb = runEffectFn3 readFileImpl file {} (handleCallback cb) +utimes file atime mtime cb = runEffectFn4 utimesImpl file (datetimeToUnixEpochTimeInSeconds atime) (datetimeToUnixEpochTimeInSeconds mtime) (handleCallback1 cb) -- | Reads the entire contents of a text file with the specified encoding. readTextFile :: Encoding -> FilePath - -> Callback String + -> Callback1 String -> Effect Unit -readTextFile encoding file cb = runEffectFn3 readFileImpl file { encoding: show encoding } (handleCallback cb) +readTextFile encoding file = readTextFile' file (readFileStringOptionsDefault { encoding = encoding }) --- | Writes a buffer to a file. -writeFile +readTextFile' :: FilePath - -> Buffer - -> Callback Unit + -> ReadFileStringOptions + -> Callback1 String -> Effect Unit -writeFile file buff cb = runEffectFn4 writeFileImpl file buff {} (handleCallback cb) +readTextFile' file options cb = runEffectFn3 readFileImpl file (readFileStringOptionsToInternal options) (handleCallback1 cb) + +-- | Reads the entire contents of a file returning the result as a raw buffer. +readFile + :: FilePath + -> Callback1 Buffer + -> Effect Unit +readFile file = readFile' file readFileBufferOptionsDefault + +readFile' + :: FilePath + -> ReadFileBufferOptions + -> Callback1 Buffer + -> Effect Unit +readFile' file options cb = runEffectFn3 readFileImpl file (readFileBufferOptionsToInternal options) (handleCallback1 cb) -- | Writes text to a file using the specified encoding. writeTextFile :: Encoding -> FilePath -> String - -> Callback Unit + -> Callback1 Unit -> Effect Unit -writeTextFile encoding file buff cb = runEffectFn4 writeFileImpl file buff { encoding: show encoding } (handleCallback cb) +writeTextFile encoding file buff = writeTextFile' file buff (writeFileStringOptionsDefault { encoding = encoding }) --- | Appends the contents of a buffer to a file. -appendFile +writeTextFile' + :: FilePath + -> String + -> WriteFileStringOptions + -> Callback1 Unit + -> Effect Unit +writeTextFile' file buff options cb = runEffectFn4 writeFileImpl file buff (writeFileStringOptionsToInternal options) (handleCallback1 cb) + +-- | Writes a buffer to a file. +writeFile + :: FilePath + -> Buffer + -> Callback1 Unit + -> Effect Unit +writeFile file buff = writeFile' file buff writeFileBufferOptionsDefault + +writeFile' :: FilePath -> Buffer - -> Callback Unit + -> WriteFileBufferOptions + -> Callback1 Unit -> Effect Unit -appendFile file buff cb = runEffectFn4 appendFileImpl file buff {} (handleCallback cb) +writeFile' file buff options cb = runEffectFn4 writeFileImpl file buff (writeFileBufferOptionsToInternal options) (handleCallback1 cb) -- | Appends text to a file using the specified encoding. appendTextFile :: Encoding -> FilePath -> String - -> Callback Unit + -> Callback1 Unit + -> Effect Unit +appendTextFile encoding file buff = appendTextFile' file buff (appendFileStringOptionsDefault { encoding = encoding }) + +appendTextFile' + :: FilePath + -> String + -> AppendFileStringOptions + -> Callback1 Unit + -> Effect Unit +appendTextFile' file buff options cb = runEffectFn4 appendFileImpl file buff (appendFileStringOptionsToInternal options) (handleCallback1 cb) + +-- | Appends a buffer to a file. +appendFile + :: FilePath + -> Buffer + -> Callback1 Unit -> Effect Unit -appendTextFile encoding file buff cb = runEffectFn4 appendFileImpl file buff { encoding: show encoding } (handleCallback cb) +appendFile file buff = appendFile' file buff appendFileBufferOptionsDefault + +appendFile' + :: FilePath + -> Buffer + -> AppendFileBufferOptions + -> Callback1 Unit + -> Effect Unit +appendFile' file buff options cb = runEffectFn4 appendFileImpl file buff (appendFileBufferOptionsToInternal options) (handleCallback1 cb) -- | Open a file asynchronously. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_open_path_flags_mode_callback) -- | for details. fdOpen :: FilePath - -> FileFlags - -> Maybe FileMode - -> Callback FileDescriptor + -> FileFlags -- default 'r' + -> Maybe FileMode -- default '0o666', TODO: use Perms? + -> Callback1 FileDescriptor -> Effect Unit -fdOpen file flags mode cb = runEffectFn4 openImpl file (fileFlagsToNode flags) (toNullable mode) (handleCallback cb) +fdOpen file flags mode cb = runEffectFn4 openImpl file (fileFlagsToNode flags) (toNullable mode) (handleCallback1 cb) -- | Read from a file asynchronously. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_read_fd_buffer_offset_length_position_callback) -- | for details. @@ -485,18 +498,27 @@ fdRead :: FileDescriptor -> Buffer -> BufferOffset - -> BufferLength - -> Maybe FilePosition - -> Callback ByteCount + -> BufferLength -- TODO: should be Maybe BufferLength + -> Maybe FilePosition -- If position is null or -1 , data will be read from the current file position + -> Callback1 ByteCount -> Effect Unit -fdRead fd buff off len pos cb = runEffectFn6 readImpl fd buff off len (toNullable pos) (handleCallback cb) +fdRead fd buff off len pos cb = runEffectFn6 readImpl fd buff off len (toNullable pos) (handleCallback1 cb) + +-- | Read from a file asynchronously. See the [Node Documentation](https://nodejs.org/docs/latest/api/fs.html#fsreadfd-options-callback) +-- | for details. +fdRead' + :: FileDescriptor + -> FdReadOptions + -> Callback1 (Tuple ByteCount Buffer) + -> Effect Unit +fdRead' fd options cb = runEffectFn3 readWithOptionsImpl fd (fdReadOptionsToInternal options) (handleCallback1Tuple cb) -- | Convenience function to fill the whole buffer from the current -- | file position. fdNext :: FileDescriptor -> Buffer - -> Callback ByteCount + -> Callback1 ByteCount -> Effect Unit fdNext fd buff cb = do sz <- size buff @@ -508,18 +530,37 @@ fdWrite :: FileDescriptor -> Buffer -> BufferOffset - -> BufferLength + -> BufferLength -- TODO: should be Maybe BufferLength -> Maybe FilePosition - -> Callback ByteCount + -> Callback1 ByteCount -> Effect Unit -fdWrite fd buff off len pos cb = runEffectFn6 writeImpl fd buff off len (toNullable pos) (handleCallback cb) +fdWrite fd buff off len pos cb = runEffectFn6 writeImpl fd buff off len (toNullable pos) (handleCallback1 cb) + +-- | Write from a file asynchronously. See the [Node Documentation](https://nodejs.org/docs/latest/api/fs.html#fswritefd-options-callback) +-- | for details. +fdWrite' + :: FileDescriptor + -> FdWriteOptions + -> Callback1 (Tuple ByteCount Buffer) + -> Effect Unit +fdWrite' fd options cb = runEffectFn3 writeWithOptionsImpl fd (fdWriteOptionsToInternal options) (handleCallback1Tuple cb) + +-- It is unsafe to use fs.write() multiple times on the same file without waiting for the callback. For this scenario, fs.createWriteStream() is recommended. +fdWriteString + :: FileDescriptor + -> String + -> Maybe FilePosition + -> Encoding + -> Callback1 (Tuple ByteCount String) + -> Effect Unit +fdWriteString fd string pos encoding cb = runEffectFn5 writeStringImpl fd string (toNullable pos) (encodingToNode encoding) (handleCallback1Tuple cb) -- | Convenience function to append the whole buffer to the current -- | file position. fdAppend :: FileDescriptor -> Buffer - -> Callback ByteCount + -> Callback1 ByteCount -> Effect Unit fdAppend fd buff cb = do sz <- size buff @@ -529,97 +570,47 @@ fdAppend fd buff cb = do -- | for details. fdClose :: FileDescriptor - -> Callback Unit - -> Effect Unit -fdClose fd cb = runEffectFn2 closeImpl fd (handleCallback cb) - -data CpForce = CpForce_False | CpForce_TrueWithoutErrorOnExit | CpForce_TrueWithErrorOnExit - -type CpOptionsInternal = - { dereference :: Boolean - , errorOnExist :: Boolean - , filter :: Nullable (Fn2 FilePath FilePath Boolean) - , force :: Boolean - , mode :: FileMode - , preserveTimestamps :: Boolean - , recursive :: Boolean - , verbatimSymlinks :: Boolean - } - -type CpOptions = - { dereference :: Boolean -- Whether to dereference symlinks - , filter :: Maybe (FilePath -> FilePath -> Boolean) - , force :: CpForce - , mode :: FileMode -- Modifiers for copy operation - , preserveTimestamps :: Boolean -- Preserve timestamps from source - , recursive :: Boolean -- Copy directories recursively - , verbatimSymlinks :: Boolean -- Skip path resolution for symlinks - } - -cpOptionsToCpOptionsInternal :: CpOptions -> CpOptionsInternal -cpOptionsToCpOptionsInternal opts = - { dereference: opts.dereference - , errorOnExist: case opts.force of - CpForce_TrueWithErrorOnExit -> true - _ -> false - , filter: toNullable $ map mkFn2 (opts.filter) - , force: case opts.force of - CpForce_False -> false - _ -> true - , mode: opts.mode - , preserveTimestamps: opts.preserveTimestamps - , recursive: opts.recursive - , verbatimSymlinks: opts.verbatimSymlinks - } - -cpOptionsDefault :: CpOptions -cpOptionsDefault = - { dereference: false - , filter: Nothing - , force: CpForce_TrueWithoutErrorOnExit - , mode: 0 - , preserveTimestamps: false - , recursive: false - , verbatimSymlinks: false - } + -> Callback1 Unit + -> Effect Unit +fdClose fd cb = runEffectFn2 closeImpl fd (handleCallback1 cb) -- | Copy a file asynchronously. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fspromises_copyfile_src_dest_mode) -- | for details. -cp :: FilePath -> FilePath -> Callback Unit -> Effect Unit +cp :: FilePath -> FilePath -> Callback1 Unit -> Effect Unit cp src dest = cp' src dest cpOptionsDefault -cp' :: FilePath -> FilePath -> CpOptions -> Callback Unit -> Effect Unit -cp' src dest opts cb = runEffectFn4 cpImpl src dest (cpOptionsToCpOptionsInternal opts) (handleCallback cb) +cp' :: FilePath -> FilePath -> CpOptions -> Callback1 Unit -> Effect Unit +cp' src dest opts cb = runEffectFn4 cpImpl src dest (cpOptionsToCpOptionsInternal opts) (handleCallback1 cb) -- | Change permissions on a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fchmod_fd_mode_callback) -- | for details. -fchmod :: FileDescriptor -> Perms -> Callback Unit -> Effect Unit -fchmod fd perms cb = runEffectFn3 fchmodImpl fd (permsToString perms) (handleCallback cb) +fchmod :: FileDescriptor -> Perms -> Callback1 Unit -> Effect Unit +fchmod fd perms cb = runEffectFn3 fchmodImpl fd (permsToString perms) (handleCallback1 cb) -- | Change ownership of a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fchown_fd_uid_gid_callback) -- | for details. -fchown :: FileDescriptor -> Int -> Int -> Callback Unit -> Effect Unit -fchown fd uid gid cb = runEffectFn4 fchownImpl fd uid gid (handleCallback cb) +fchown :: FileDescriptor -> Int -> Int -> Callback1 Unit -> Effect Unit +fchown fd uid gid cb = runEffectFn4 fchownImpl fd uid gid (handleCallback1 cb) -- | Synchronize a file's in-core state with storage. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fdatasync_fd_callback) -- | for details. -fdatasync :: FileDescriptor -> Callback Unit -> Effect Unit -fdatasync fd cb = runEffectFn2 fdatasyncImpl fd (handleCallback cb) +fdatasync :: FileDescriptor -> Callback1 Unit -> Effect Unit +fdatasync fd cb = runEffectFn2 fdatasyncImpl fd (handleCallback1 cb) -- | Get file status information. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fstat_fd_callback) -- | for details. -fstat :: FileDescriptor -> Callback Stats -> Effect Unit -fstat fd cb = runEffectFn2 fstatImpl fd (handleCallback cb) +fstat :: FileDescriptor -> Callback1 Stats -> Effect Unit +fstat fd cb = runEffectFn2 fstatImpl fd (handleCallback1 cb) -- | Flushes a file descriptor to disk. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_fsync_fd_callback) -- | for details. -fsync :: FileDescriptor -> Callback Unit -> Effect Unit -fsync fd cb = runEffectFn2 fsyncImpl fd (handleCallback cb) +fsync :: FileDescriptor -> Callback1 Unit -> Effect Unit +fsync fd cb = runEffectFn2 fsyncImpl fd (handleCallback1 cb) -- | Truncate a file to a specified length. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_ftruncate_fd_len_callback) -- | for details. -ftruncate :: FileDescriptor -> Int -> Callback Unit -> Effect Unit -ftruncate fd len cb = runEffectFn3 ftruncateImpl fd len (handleCallback cb) +ftruncate :: FileDescriptor -> Int -> Callback1 Unit -> Effect Unit +ftruncate fd len cb = runEffectFn3 ftruncateImpl fd len (handleCallback1 cb) -- | Change file timestamps for a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_futimes_fd_atime_mtime_callback) -- | for details. @@ -627,33 +618,33 @@ futimes :: FilePath -> DateTime -> DateTime - -> Callback Unit + -> Callback1 Unit -> Effect Unit -futimes file atime mtime cb = runEffectFn4 lutimesImpl file (datetimeToUnixEpochTimeInSeconds atime) (datetimeToUnixEpochTimeInSeconds mtime) (handleCallback cb) +futimes file atime mtime cb = runEffectFn4 lutimesImpl file (datetimeToUnixEpochTimeInSeconds atime) (datetimeToUnixEpochTimeInSeconds mtime) (handleCallback1 cb) -- | Perform pattern matching in file paths. See the [Node Documentation](https://nodejs.org/api/glob.html#globglob_pattern_options_callback) -- | for details. -glob :: Array FilePath -> Callback (Array FilePath) -> Effect Unit -glob pattern = glob' pattern { cwd: Nothing, exclude: Nothing } +glob :: Array FilePath -> Callback1 (Array FilePath) -> Effect Unit +glob pattern = glob' pattern globFilePathOptionsDefault -glob' :: Array FilePath -> { cwd :: Maybe FilePath, exclude :: Maybe (FilePath -> Boolean) } -> Callback (Array FilePath) -> Effect Unit -glob' pattern { cwd, exclude } cb = runEffectFn3 globImpl pattern { cwd: toNullable cwd, exclude: toNullable exclude, withFileTypes: false } (handleCallback cb) +glob' :: Array FilePath -> GlobFilePathOptions -> Callback1 (Array FilePath) -> Effect Unit +glob' pattern options cb = runEffectFn3 globImpl pattern (globFilePathOptionsToInternal options) (handleCallback1 cb) -globDirent :: Array FilePath -> Callback (Array (Dirent DirentNameTypeString)) -> Effect Unit -globDirent pattern = globDirent' pattern { cwd: Nothing, exclude: Nothing } +globDirent :: Array FilePath -> Callback1 (Array (Dirent DirentNameTypeString)) -> Effect Unit +globDirent pattern = globDirent' pattern globDirentOptionsDefault -globDirent' :: Array FilePath -> { cwd :: Maybe FilePath, exclude :: Maybe (Dirent DirentNameTypeString -> Boolean) } -> Callback (Array (Dirent DirentNameTypeString)) -> Effect Unit -globDirent' pattern { cwd, exclude } cb = runEffectFn3 globImpl pattern { cwd: toNullable cwd, exclude: toNullable exclude, withFileTypes: true } (handleCallback cb) +globDirent' :: Array FilePath -> GlobDirentOptions -> Callback1 (Array (Dirent DirentNameTypeString)) -> Effect Unit +globDirent' pattern options cb = runEffectFn3 globImpl pattern (globDirentOptionsToInternal options) (handleCallback1 cb) -- | Change permissions on a symbolic link. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_lchmod_path_mode_callback) -- | for details. -lchmod :: FilePath -> Perms -> Callback Unit -> Effect Unit -lchmod path perms cb = runEffectFn3 lchmodImpl path (permsToString perms) (handleCallback cb) +lchmod :: FilePath -> Perms -> Callback1 Unit -> Effect Unit +lchmod path perms cb = runEffectFn3 lchmodImpl path (permsToString perms) (handleCallback1 cb) -- | Change ownership of a symbolic link. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_lchown_path_uid_gid_callback) -- | for details. -lchown :: FilePath -> Int -> Int -> Callback Unit -> Effect Unit -lchown path uid gid cb = runEffectFn4 lchownImpl path uid gid (handleCallback cb) +lchown :: FilePath -> Int -> Int -> Callback1 Unit -> Effect Unit +lchown path uid gid cb = runEffectFn4 lchownImpl path uid gid (handleCallback1 cb) -- | Change timestamps for a symbolic link. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_lutimes_path_atime_mtime_callback) -- | for details. @@ -661,41 +652,36 @@ lutimes :: FilePath -> DateTime -> DateTime - -> Callback Unit + -> Callback1 Unit -> Effect Unit -lutimes file atime mtime cb = runEffectFn4 lutimesImpl file (datetimeToUnixEpochTimeInSeconds atime) (datetimeToUnixEpochTimeInSeconds mtime) (handleCallback cb) +lutimes file atime mtime cb = runEffectFn4 lutimesImpl file (datetimeToUnixEpochTimeInSeconds atime) (datetimeToUnixEpochTimeInSeconds mtime) (handleCallback1 cb) -- | TODO: path - Buffer Url, returns Promise -- | Open a file as a blob. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_class_filehandle) -- | for details. -- openAsBlob :: FilePath -> Promise Blob -> Effect Unit --- openAsBlob path cb = runEffectFn2 openAsBlobImpl path (handleCallback cb) - -type OpendirOptions = { encoding :: Encoding, bufferSize :: Int, recursive :: Boolean } - -opendirOptionsDefault :: OpendirOptions -opendirOptionsDefault = { bufferSize: 32, recursive: false, encoding: UTF8 } +-- openAsBlob path cb = runEffectFn2 openAsBlobImpl path (handleCallback1 cb) -- | Open a directory. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_opendir_path_options_callback) -- | for details. -opendir' :: FilePath -> OpendirOptions -> Callback Dir -> Effect Unit -opendir' path { encoding, bufferSize, recursive } cb = runEffectFn3 opendirImpl path { encoding: encodingToNode encoding, bufferSize, recursive } (handleCallback cb) +opendir' :: FilePath -> OpendirOptions -> Callback1 Dir -> Effect Unit +opendir' path options cb = runEffectFn3 opendirImpl path (opendirOptionsToInternal options) (handleCallback1 cb) -- | Open a directory. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_opendir_path_options_callback) -- | for details. -- | NOTE: encoding: 'buffer' is not supported, will throw error "TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of Buffer" -opendir :: FilePath -> Callback Dir -> Effect Unit +opendir :: FilePath -> Callback1 Dir -> Effect Unit opendir path = opendir' path opendirOptionsDefault -- | Read from a file descriptor into a buffer array. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_readv_fd_buffers_position_callback) -- | for details. -readv :: FileDescriptor -> Array Buffer -> Maybe FilePosition -> Callback (Tuple ByteCount (Array Buffer)) -> Effect Unit -readv fd buffers position cb = runEffectFn4 readvImpl fd buffers (toNullable position) (handleCallback2 cb) +readv :: FileDescriptor -> Array Buffer -> Maybe FilePosition -> Callback1 (Tuple ByteCount (Array Buffer)) -> Effect Unit +readv fd buffers position cb = runEffectFn4 readvImpl fd buffers (toNullable position) (handleCallback1Tuple cb) -- | Get file system statistics. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_statfs_path_callback) -- | for details. -statfs :: FilePath -> Callback Stats -> Effect Unit -statfs path cb = runEffectFn2 statfsImpl path (handleCallback cb) +statfs :: FilePath -> Callback1 Stats -> Effect Unit +statfs path cb = runEffectFn2 statfsImpl path (handleCallback1 cb) -- -- | Stop watching a file for changes. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_unwatchfile_filename_listener) -- -- | for details. @@ -714,5 +700,5 @@ statfs path cb = runEffectFn2 statfsImpl path (handleCallback cb) -- | Write from an array of buffers to a file descriptor. See the [Node Documentation](https://nodejs.org/api/fs.html#fs_fs_writev_fd_buffers_position_callback) -- | for details. -writev :: FileDescriptor -> Array Buffer -> Maybe FilePosition -> Callback (Tuple ByteCount (Array Buffer)) -> Effect Unit -writev fd buffers position cb = runEffectFn4 writevImpl fd buffers (toNullable position) (handleCallback2 cb) +writev :: FileDescriptor -> Array Buffer -> Maybe FilePosition -> Callback1 (Tuple ByteCount (Array Buffer)) -> Effect Unit +writev fd buffers position cb = runEffectFn4 writevImpl fd buffers (toNullable position) (handleCallback1Tuple cb) diff --git a/src/Node/FS/Constants.purs b/src/Node/FS/Constants.purs index c6bbcab..24f4880 100644 --- a/src/Node/FS/Constants.purs +++ b/src/Node/FS/Constants.purs @@ -88,3 +88,4 @@ fileFlagsToNode ff = case ff of AX -> "ax" A_PLUS -> "a+" AX_PLUS -> "ax+" + diff --git a/src/Node/FS/Dir.purs b/src/Node/FS/Dir.purs index 1ccd30b..18b9a25 100644 --- a/src/Node/FS/Dir.purs +++ b/src/Node/FS/Dir.purs @@ -2,38 +2,29 @@ module Node.FS.Dir where import Prelude -import Data.Either (Either(..)) -import Data.Maybe (Maybe(..)) +import Data.Either (Either) +import Data.Maybe (Maybe) import Data.Nullable (Nullable, toMaybe) import Effect (Effect) import Effect.Exception (Error) -import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, mkEffectFn2, runEffectFn1, runEffectFn2) +import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, runEffectFn1, runEffectFn2) import Node.FS.Dirent (Dirent, DirentNameTypeString) +import Node.FS.Internal.Utils (Callback0, JSCallback0, JSCallback1, handleCallback1) import Node.Path (FilePath) -type JSCallback1 = EffectFn1 (Nullable Error) Unit -type JSCallback2 a = EffectFn2 (Nullable Error) a Unit -type Callback1 = Maybe Error -> Effect Unit -type Callback2 a = Either Error a -> Effect Unit - -handleCallback2 :: forall a. Callback2 a -> JSCallback2 a -handleCallback2 cb = mkEffectFn2 \err a -> case toMaybe err of - Nothing -> cb (Right a) - Just err' -> cb (Left err') - -- Foreign imports for the Dir class foreign import data Dir :: Type -foreign import closeImpl :: EffectFn2 Dir JSCallback1 Unit +foreign import closeImpl :: EffectFn2 Dir JSCallback0 Unit foreign import closeSyncImpl :: EffectFn1 Dir Unit -foreign import readImpl :: EffectFn2 Dir (JSCallback2 (Nullable (Dirent DirentNameTypeString))) Unit +foreign import readImpl :: EffectFn2 Dir (JSCallback1 (Nullable (Dirent DirentNameTypeString))) Unit foreign import readSyncImpl :: EffectFn1 Dir (Nullable (Dirent DirentNameTypeString)) -- | Get the path of this directory as was provided to fs.opendir(), fs.opendirSync(), or fsPromises.opendir(). foreign import path :: Dir -> FilePath -- | Asynchronously close the directory's underlying resource handle. -close :: Dir -> Callback1 -> Effect Unit +close :: Dir -> Callback0 -> Effect Unit close dir callback = runEffectFn2 closeImpl dir (mkEffectFn1 $ (callback <<< toMaybe)) -- | Synchronously close the directory's underlying resource handle. @@ -42,7 +33,7 @@ closeSync = runEffectFn1 closeSyncImpl -- | Asynchronously read the next directory entry. read :: Dir -> (Either Error (Maybe (Dirent DirentNameTypeString)) -> Effect Unit) -> Effect Unit -read dir callback = runEffectFn2 readImpl dir (handleCallback2 (callback <<< map toMaybe)) +read dir callback = runEffectFn2 readImpl dir (handleCallback1 (callback <<< map toMaybe)) -- | Synchronously read the next directory entry. readSync :: Dir -> Effect (Maybe (Dirent DirentNameTypeString)) diff --git a/src/Node/FS/Internal/AffUtils.purs b/src/Node/FS/Internal/AffUtils.purs new file mode 100644 index 0000000..53d7957 --- /dev/null +++ b/src/Node/FS/Internal/AffUtils.purs @@ -0,0 +1,71 @@ +module Node.FS.Internal.AffUtils where + +import Prelude + +import Effect (Effect) +import Effect.Aff (Aff, makeAff, nonCanceler) +import Node.FS.Internal.Utils (Callback1) + +toAff + :: forall a + . (Callback1 a -> Effect Unit) + -> Aff a +toAff p = makeAff \k -> p k $> nonCanceler + +toAff1 + :: forall a x + . (x -> Callback1 a -> Effect Unit) + -> x + -> Aff a +toAff1 f a = toAff (f a) + +toAff2 + :: forall a x y + . (x -> y -> Callback1 a -> Effect Unit) + -> x + -> y + -> Aff a +toAff2 f a b = toAff (f a b) + +toAff3 + :: forall a x y z + . (x -> y -> z -> Callback1 a -> Effect Unit) + -> x + -> y + -> z + -> Aff a +toAff3 f a b c = toAff (f a b c) + +toAff4 + :: forall a v x y z + . (v -> x -> y -> z -> Callback1 a -> Effect Unit) + -> v + -> x + -> y + -> z + -> Aff a +toAff4 f a b c d = toAff (f a b c d) + +toAff5 + :: forall a w v x y z + . (w -> v -> x -> y -> z -> Callback1 a -> Effect Unit) + -> w + -> v + -> x + -> y + -> z + -> Aff a +toAff5 f a b c d e = toAff (f a b c d e) + +toAff6 + :: forall a w v x y z t + . (w -> v -> x -> y -> z -> t -> Callback1 a -> Effect Unit) + -> w + -> v + -> x + -> y + -> z + -> t + -> Aff a +toAff6 f a b c d e t = toAff (f a b c d e t) + diff --git a/src/Node/FS/Internal/Utils.purs b/src/Node/FS/Internal/Utils.purs new file mode 100644 index 0000000..3f488f2 --- /dev/null +++ b/src/Node/FS/Internal/Utils.purs @@ -0,0 +1,42 @@ +module Node.FS.Internal.Utils where + +import Prelude + +import Data.DateTime (DateTime) +import Data.DateTime.Instant (fromDateTime, unInstant) +import Data.Either (Either(..)) +import Data.Int (round) +import Data.Maybe (Maybe(..)) +import Data.Nullable (Nullable, toMaybe) +import Data.Time.Duration (Milliseconds(..)) +import Data.Tuple (Tuple(..)) +import Effect (Effect) +import Effect.Exception (Error) +import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, mkEffectFn2, mkEffectFn3) + +type JSCallback0 = EffectFn1 (Nullable Error) Unit +type JSCallback1 a = EffectFn2 (Nullable Error) a Unit +type JSCallback2 a b = EffectFn3 (Nullable Error) a b Unit + +-- | Type synonym for callback functions. +type Callback0 = Maybe Error -> Effect Unit +type Callback1 a = Either Error a -> Effect Unit + +handleCallback1 :: forall a. Callback1 a -> JSCallback1 a +handleCallback1 cb = mkEffectFn2 \err a -> case toMaybe err of + Nothing -> cb (Right a) + Just err' -> cb (Left err') + +handleCallback1Tuple :: forall a b. Callback1 (Tuple a b) -> JSCallback2 a b +handleCallback1Tuple cb = mkEffectFn3 \err a b -> case toMaybe err of + Nothing -> cb (Right (Tuple a b)) + Just err' -> cb (Left err') + +---------------------- + +datetimeToUnixEpochTimeInSeconds :: DateTime -> Int +datetimeToUnixEpochTimeInSeconds date = ms (toEpochMilliseconds date) / 1000 + where + ms (Milliseconds n) = round n + toEpochMilliseconds = unInstant <<< fromDateTime + diff --git a/src/Node/FS/Options.purs b/src/Node/FS/Options.purs new file mode 100644 index 0000000..5af6a9a --- /dev/null +++ b/src/Node/FS/Options.purs @@ -0,0 +1,329 @@ +module Node.FS.Options where + +import Node.FS.Types (BufferLength, BufferOffset, FileMode, FilePosition) +import Prelude + +import Data.Function.Uncurried (Fn2, mkFn2) +import Data.Maybe (Maybe(..)) +import Data.Nullable (Nullable, toNullable) +import Data.Nullable as Nullable +import Foreign (Foreign) +import Foreign as Foreign +import Node.Buffer (Buffer) +import Node.Encoding (Encoding(..), encodingToNode) +import Node.FS.Constants (FileFlags(..), fileFlagsToNode) +import Node.FS.Dirent (Dirent, DirentNameTypeString) +import Node.FS.Perms (Perms, all, mkPerms, permsToString, read, write) +import Node.Path (FilePath) + +type RmdirOptions = { maxRetries :: Int, retryDelay :: Int } + +rmdirOptionsDefault :: RmdirOptions +rmdirOptionsDefault = { maxRetries: 0, retryDelay: 100 } + +type RmOptions = { force :: Boolean, maxRetries :: Int, recursive :: Boolean, retryDelay :: Int } + +rmOptionsDefault :: RmOptions +rmOptionsDefault = { force: false, maxRetries: 100, recursive: false, retryDelay: 1000 } + +---------- + +type MkdirOptionsInternal = { recursive :: Boolean, mode :: String } +type MkdirOptions = { recursive :: Boolean, mode :: Perms } + +mkdirOptionsDefault :: MkdirOptions +mkdirOptionsDefault = { recursive: false, mode: mkPerms all all all } + +mkdirOptionsToInternal :: MkdirOptions -> MkdirOptionsInternal +mkdirOptionsToInternal { recursive, mode } = { recursive, mode: permsToString mode } + +--------- + +type RealpathOptionsInternal = { encoding :: String } +type RealpathOptions = { encoding :: Encoding } + +realpathOptionsDefault :: RealpathOptions +realpathOptionsDefault = { encoding: UTF8 } + +realpathOptionsToInternal :: RealpathOptions -> RealpathOptionsInternal +realpathOptionsToInternal { encoding } = { encoding: encodingToNode encoding } + +--------- +type ReaddirOptionsInternal = + { encoding :: Foreign -- encoding string or "buffer" + , recursive :: Boolean + , withFileTypes :: Boolean + } + +type ReaddirFilePathOptions = { recursive :: Boolean, encoding :: Encoding } + +readdirFilePathOptionsDefault :: ReaddirFilePathOptions +readdirFilePathOptionsDefault = { recursive: false, encoding: UTF8 } + +readdirFilePathOptionsToInternal :: ReaddirFilePathOptions -> ReaddirOptionsInternal +readdirFilePathOptionsToInternal { recursive, encoding } = { recursive, encoding: Foreign.unsafeToForeign $ encodingToNode encoding, withFileTypes: false } + +type ReaddirBufferOptions = { recursive :: Boolean } + +readdirBufferOptionsDefault :: ReaddirBufferOptions +readdirBufferOptionsDefault = { recursive: false } + +readdirBufferOptionsToInternal :: ReaddirBufferOptions -> ReaddirOptionsInternal +readdirBufferOptionsToInternal { recursive } = { recursive, encoding: Foreign.unsafeToForeign "buffer", withFileTypes: false } + +type ReaddirDirentOptions = { recursive :: Boolean, encoding :: Encoding } + +readdirDirentOptionsDefault :: ReaddirDirentOptions +readdirDirentOptionsDefault = { recursive: false, encoding: UTF8 } + +readdirDirentOptionsToInternal :: ReaddirDirentOptions -> ReaddirOptionsInternal +readdirDirentOptionsToInternal { recursive, encoding } = { recursive, encoding: Foreign.unsafeToForeign $ encodingToNode encoding, withFileTypes: true } + +type ReaddirDirentBufferOptions = { recursive :: Boolean } + +readdirDirentBufferOptionsDefault :: ReaddirDirentBufferOptions +readdirDirentBufferOptionsDefault = { recursive: false } + +readdirDirentBufferOptionsToInternal :: ReaddirDirentBufferOptions -> ReaddirOptionsInternal +readdirDirentBufferOptionsToInternal { recursive } = { recursive, encoding: Foreign.unsafeToForeign "buffer", withFileTypes: true } + +--------- +type ReadFileOptionsInternal = + { encoding :: Nullable String -- if null - returns Buffer, if encoding string - String https://nodejs.org/docs/latest/api/fs.html#fsreadfilepath-options-callback + , flag :: String + -- , signal :: Nullable AbortSignal + } + +type ReadFileStringOptions = { flag :: FileFlags, encoding :: Encoding } + +readFileStringOptionsDefault :: ReadFileStringOptions +readFileStringOptionsDefault = { flag: R, encoding: UTF8 } + +readFileStringOptionsToInternal :: ReadFileStringOptions -> ReadFileOptionsInternal +readFileStringOptionsToInternal { flag, encoding } = { flag: fileFlagsToNode flag, encoding: Nullable.notNull $ encodingToNode encoding } + +type ReadFileBufferOptions = { flag :: FileFlags } + +readFileBufferOptionsDefault :: ReadFileBufferOptions +readFileBufferOptionsDefault = { flag: R } + +readFileBufferOptionsToInternal :: ReadFileBufferOptions -> ReadFileOptionsInternal +readFileBufferOptionsToInternal { flag } = { flag: fileFlagsToNode flag, encoding: Nullable.null } + +--------- +type WriteFileOptionsInternal = + { encoding :: Nullable String -- The encoding option is ignored if data is a buffer + , mode :: String + , flag :: String -- See support of file system flags. Default: 'w'. + , flush :: Boolean -- If all data is successfully written to the file, and flush is true, fs.fsync() is used to flush the data. Default: false. + -- , signal :: Nullable AbortSignal -- allows aborting an in-progress writeFile + } + +type WriteFileStringOptions = + { encoding :: Encoding + , mode :: Perms + , flag :: FileFlags + , flush :: Boolean + } + +writeFileStringOptionsDefault :: WriteFileStringOptions +writeFileStringOptionsDefault = + { encoding: UTF8 + , mode: mkPerms (read + write) (read + write) (read + write) + , flag: W + , flush: false + } + +writeFileStringOptionsToInternal :: WriteFileStringOptions -> WriteFileOptionsInternal +writeFileStringOptionsToInternal { encoding, mode, flag, flush } = { encoding: Nullable.notNull $ encodingToNode encoding, mode: permsToString mode, flag: fileFlagsToNode flag, flush } + +type WriteFileBufferOptions = + { mode :: Perms + , flag :: FileFlags + , flush :: Boolean + } + +writeFileBufferOptionsDefault :: WriteFileBufferOptions +writeFileBufferOptionsDefault = + { mode: mkPerms (read + write) (read + write) (read + write) + , flag: W + , flush: false + } + +writeFileBufferOptionsToInternal :: WriteFileBufferOptions -> WriteFileOptionsInternal +writeFileBufferOptionsToInternal { mode, flag, flush } = { mode: permsToString mode, flag: fileFlagsToNode flag, flush, encoding: Nullable.null } + +--------- +type AppendFileOptionsInternal = + { encoding :: Nullable String -- The encoding option is ignored if data is a buffer + , mode :: String + , flag :: String -- See support of file system flags. Default: 'w'. + , flush :: Boolean -- If all data is successfully written to the file, and flush is true, fs.fsync() is used to flush the data. Default: false. + } + +type AppendFileStringOptions = + { encoding :: Encoding + , mode :: Perms + , flag :: FileFlags + , flush :: Boolean + } + +appendFileStringOptionsDefault :: AppendFileStringOptions +appendFileStringOptionsDefault = + { encoding: UTF8 + , mode: mkPerms (read + write) (read + write) (read + write) + , flag: A + , flush: false + } + +appendFileStringOptionsToInternal :: AppendFileStringOptions -> AppendFileOptionsInternal +appendFileStringOptionsToInternal { encoding, mode, flag, flush } = { encoding: Nullable.notNull $ encodingToNode encoding, mode: permsToString mode, flag: fileFlagsToNode flag, flush } + +type AppendFileBufferOptions = + { mode :: Perms + , flag :: FileFlags + , flush :: Boolean + } + +appendFileBufferOptionsDefault :: AppendFileBufferOptions +appendFileBufferOptionsDefault = + { mode: mkPerms (read + write) (read + write) (read + write) + , flag: A + , flush: false + } + +appendFileBufferOptionsToInternal :: AppendFileBufferOptions -> AppendFileOptionsInternal +appendFileBufferOptionsToInternal { mode, flag, flush } = { mode: permsToString mode, flag: fileFlagsToNode flag, flush, encoding: Nullable.null } + +--------- +type FdReadOptionsInternal = + { buffer :: Nullable Buffer -- Default: `Buffer.alloc(16384)` + , offset :: BufferOffset -- Default: `0` + , length :: Nullable BufferLength -- Default: buffer.byteLength - offset + , position :: Nullable FilePosition + } + +type FdReadOptions = + { buffer :: Maybe Buffer + , offset :: BufferOffset + , length :: Maybe BufferLength + , position :: Maybe FilePosition -- If position is null or -1 , data will be read from the current file position, and the file position will be updated. If position is a non-negative integer, the file position will be unchanged. + } + +fdReadOptionsDefault :: FdReadOptions +fdReadOptionsDefault = + { buffer: Nothing + , offset: 0 + , length: Nothing + , position: Nothing + } + +fdReadOptionsToInternal :: FdReadOptions -> FdReadOptionsInternal +fdReadOptionsToInternal { buffer, offset, length, position } = { buffer: Nullable.toNullable buffer, offset, length: Nullable.toNullable length, position: Nullable.toNullable position } + +--------- +type FdWriteOptionsInternal = + { offset :: BufferOffset -- Default: `0` + , length :: Nullable BufferLength -- Default: buffer.byteLength - offset + , position :: Nullable FilePosition + } + +type FdWriteOptions = + { offset :: BufferOffset + , length :: Maybe BufferLength + , position :: Maybe FilePosition -- If position is null or -1 , data will be write from the current file position, and the file position will be updated. If position is a non-negative integer, the file position will be unchanged. + } + +fdWriteOptionsDefault :: FdWriteOptions +fdWriteOptionsDefault = + { offset: 0 + , length: Nothing + , position: Nothing + } + +fdWriteOptionsToInternal :: FdWriteOptions -> FdWriteOptionsInternal +fdWriteOptionsToInternal { offset, length, position } = { offset, length: Nullable.toNullable length, position: Nullable.toNullable position } + +------------------- + +type CpOptionsInternal = + { dereference :: Boolean + , errorOnExist :: Boolean + , filter :: Nullable (Fn2 FilePath FilePath Boolean) + , force :: Boolean + , mode :: FileMode + , preserveTimestamps :: Boolean + , recursive :: Boolean + , verbatimSymlinks :: Boolean + } + +data CpForce = CpForce_False | CpForce_TrueWithoutErrorOnExit | CpForce_TrueWithErrorOnExit + +type CpOptions = + { dereference :: Boolean -- Whether to dereference symlinks + , filter :: Maybe (FilePath -> FilePath -> Boolean) + , force :: CpForce + , mode :: FileMode -- Modifiers for copy operation + , preserveTimestamps :: Boolean -- Preserve timestamps from source + , recursive :: Boolean -- Copy directories recursively + , verbatimSymlinks :: Boolean -- Skip path resolution for symlinks + } + +cpOptionsDefault :: CpOptions +cpOptionsDefault = + { dereference: false + , filter: Nothing + , force: CpForce_TrueWithoutErrorOnExit + , mode: 0 + , preserveTimestamps: false + , recursive: false + , verbatimSymlinks: false + } + +cpOptionsToCpOptionsInternal :: CpOptions -> CpOptionsInternal +cpOptionsToCpOptionsInternal opts = + { dereference: opts.dereference + , errorOnExist: case opts.force of + CpForce_TrueWithErrorOnExit -> true + _ -> false + , filter: toNullable $ map mkFn2 (opts.filter) + , force: case opts.force of + CpForce_False -> false + _ -> true + , mode: opts.mode + , preserveTimestamps: opts.preserveTimestamps + , recursive: opts.recursive + , verbatimSymlinks: opts.verbatimSymlinks + } + +------------------ + +type GlobOptionsInternal filepathOrDirent = { cwd :: Nullable FilePath, exclude :: Nullable (filepathOrDirent -> Boolean), withFileTypes :: Boolean } + +type GlobFilePathOptions = { cwd :: Maybe FilePath, exclude :: Maybe (FilePath -> Boolean) } + +globFilePathOptionsDefault :: GlobFilePathOptions +globFilePathOptionsDefault = { cwd: Nothing, exclude: Nothing } + +globFilePathOptionsToInternal :: GlobFilePathOptions -> GlobOptionsInternal FilePath +globFilePathOptionsToInternal { cwd, exclude } = { cwd: toNullable cwd, exclude: toNullable exclude, withFileTypes: false } + +type GlobDirentOptions = { cwd :: Maybe FilePath, exclude :: Maybe (Dirent DirentNameTypeString -> Boolean) } + +globDirentOptionsDefault :: GlobDirentOptions +globDirentOptionsDefault = { cwd: Nothing, exclude: Nothing } + +globDirentOptionsToInternal :: GlobDirentOptions -> GlobOptionsInternal (Dirent DirentNameTypeString) +globDirentOptionsToInternal { cwd, exclude } = { cwd: toNullable cwd, exclude: toNullable exclude, withFileTypes: true } + +------------------ + +type OpendirOptionsInternal = { encoding :: String, bufferSize :: Int, recursive :: Boolean } +type OpendirOptions = { encoding :: Encoding, bufferSize :: Int, recursive :: Boolean } + +opendirOptionsDefault :: OpendirOptions +opendirOptionsDefault = { bufferSize: 32, recursive: false, encoding: UTF8 } + +opendirOptionsToInternal :: OpendirOptions -> OpendirOptionsInternal +opendirOptionsToInternal { encoding, bufferSize, recursive } = { encoding: encodingToNode encoding, bufferSize, recursive } + diff --git a/src/Node/FS/Sync.js b/src/Node/FS/Sync.js index 166b656..89e3178 100644 --- a/src/Node/FS/Sync.js +++ b/src/Node/FS/Sync.js @@ -27,20 +27,19 @@ export { writeSync as writeSyncImpl, fsyncSync as fsyncSyncImpl, closeSync as closeSyncImpl, - // TODO: implement - // cpSync as cpSyncImpl, - // fchmodSync as fchmodSyncImpl, - // fchownSync as fchownSyncImpl, - // fdatasyncSync as fdatasyncSyncImpl, - // fstatSync as fstatSyncImpl, - // ftruncateSync as ftruncateSyncImpl, - // futimesSync as futimesSyncImpl, - // globSync as globSyncImpl, - // lchmodSync as lchmodSyncImpl, - // lchownSync as lchownSyncImpl, - // lutimesSync as lutimesSyncImpl, - // opendirSync as opendirSyncImpl, - // readvSync as readvSyncImpl, - // statfsSync as statfsSyncImpl, - // writevSync as writevSyncImpl + cpSync as cpSyncImpl, + fchmodSync as fchmodSyncImpl, + fchownSync as fchownSyncImpl, + fdatasyncSync as fdatasyncSyncImpl, + fstatSync as fstatSyncImpl, + ftruncateSync as ftruncateSyncImpl, + futimesSync as futimesSyncImpl, + globSync as globSyncImpl, + lchmodSync as lchmodSyncImpl, + lchownSync as lchownSyncImpl, + lutimesSync as lutimesSyncImpl, + opendirSync as opendirSyncImpl, + readvSync as readvSyncImpl, + statfsSync as statfsSyncImpl, + writevSync as writevSyncImpl } from "node:fs"; diff --git a/src/Node/FS/Types.purs b/src/Node/FS/Types.purs new file mode 100644 index 0000000..94e5cc9 --- /dev/null +++ b/src/Node/FS/Types.purs @@ -0,0 +1,34 @@ +module Node.FS.Types where + +import Prelude + +import Data.Nullable (Nullable) +import Data.Nullable as Nullable + +foreign import data FileDescriptor :: Type + +type FileMode = Int +type FilePosition = Int +type BufferLength = Int +type BufferOffset = Int +type ByteCount = Int + +-- | Symlink varieties. +data SymlinkType = FileLink | DirLink | JunctionLink | AutodetectLink + +-- | Convert a `SymlinkType` to a `String` in the format expected by the +-- | Node.js filesystem API. +symlinkTypeToNode :: SymlinkType -> Nullable String +symlinkTypeToNode ty = case ty of + FileLink -> Nullable.notNull "file" + DirLink -> Nullable.notNull "dir" + JunctionLink -> Nullable.notNull "junction" + AutodetectLink -> Nullable.null + +instance showSymlinkType :: Show SymlinkType where + show FileLink = "FileLink" + show DirLink = "DirLink" + show JunctionLink = "JunctionLink" + show AutodetectLink = "AutodetectLink" + +derive instance eqSymlinkType :: Eq SymlinkType diff --git a/test/Test/Node/FS/OpendirAndDir.purs b/test/Test/Node/FS/OpendirAndDir.purs index 85487ee..8d49551 100644 --- a/test/Test/Node/FS/OpendirAndDir.purs +++ b/test/Test/Node/FS/OpendirAndDir.purs @@ -11,7 +11,8 @@ import Effect.Class (liftEffect) import Effect.Console (log) import Effect.Exception (Error) import Node.Encoding (Encoding(..)) -import Node.FS.Aff (opendirOptionsDefault, rmOptionsDefault) +import Node.FS.Aff +import Node.FS.Options import Node.FS.Aff as A import Node.FS.Aff.Dir (close, entries, read) import Node.FS.Dirent (Dirent, DirentNameTypeString) diff --git a/test/Test/Node/FS/Sync.purs b/test/Test/Node/FS/Sync.purs index 994ddc6..25327eb 100644 --- a/test/Test/Node/FS/Sync.purs +++ b/test/Test/Node/FS/Sync.purs @@ -11,12 +11,13 @@ import Effect.Exception (Error, catchException, error, message, throw, throwExce import Node.Buffer as Buffer import Node.Encoding (Encoding(..)) import Node.FS (FileFlags(..), SymlinkType(..)) -import Node.FS.Aff (rmOptionsDefault) +import Node.FS.Aff import Node.FS.Async as A import Node.FS.Constants (copyFile_EXCL, r_OK, w_OK) import Node.FS.Perms (mkPerms, permsAll) import Node.FS.Perms as Perms import Node.FS.Stats (statusChangedTime, accessedTime, modifiedTime, isSymbolicLink, isSocket, isFIFO, isCharacterDevice, isBlockDevice, isDirectory, isFile) +import Node.FS.Options import Node.FS.Sync (chmod) import Node.FS.Sync as S import Node.Path as Path