diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac4389..19be5fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ Breaking changes: New features: - Integrate `node-fs-aff` into library (#75 by @JordanMartinez) +- Remove `StatsObj` and reimplement bindings to `Stats` object (#76 by @JordanMartinez) + + Previously, one could write the following to get a value on the `Stats` object: + ```purs + getGid :: Stats -> Number + getGid (Stats obj) = obj.gid + ``` + + This record interface was removed as the underlying value is not a record + that can be copied and modified as such. Now, one must call the corresponding function: + ```purs + getGid :: Stats -> Number + getGid s = Stats.gid s + ``` Bugfixes: diff --git a/src/Node/FS/Async.purs b/src/Node/FS/Async.purs index 05b04f1..cfab806 100644 --- a/src/Node/FS/Async.purs +++ b/src/Node/FS/Async.purs @@ -57,7 +57,7 @@ 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.Perms (Perms, permsToString, all, mkPerms) -import Node.FS.Stats (StatsObj, Stats(..)) +import Node.FS.Stats (Stats) import Node.Path (FilePath) type JSCallback a = EffectFn2 (Nullable Error) a Unit @@ -99,8 +99,8 @@ 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 StatsObj) Unit -foreign import lstatImpl :: EffectFn2 FilePath (JSCallback StatsObj) 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 String (JSCallback Unit) Unit foreign import readlinkImpl :: EffectFn2 FilePath (JSCallback FilePath) Unit @@ -157,7 +157,7 @@ stat :: FilePath -> Callback Stats -> Effect Unit -stat file cb = runEffectFn2 statImpl file (handleCallback $ cb <<< map Stats) +stat file cb = runEffectFn2 statImpl file (handleCallback $ 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, @@ -166,7 +166,7 @@ lstat :: FilePath -> Callback Stats -> Effect Unit -lstat file cb = runEffectFn2 lstatImpl file (handleCallback $ cb <<< map Stats) +lstat file cb = runEffectFn2 lstatImpl file (handleCallback $ cb) -- | Creates a link to an existing file. link diff --git a/src/Node/FS/Stats.js b/src/Node/FS/Stats.js index e6e6148..a4bbddf 100644 --- a/src/Node/FS/Stats.js +++ b/src/Node/FS/Stats.js @@ -1,5 +1,27 @@ export { inspect as showStatsObj } from "util"; -export function statsMethod(m, s) { - return s[m](); -} +export const isBlockDeviceImpl = (s) => s.isBlockDevice(); +export const isCharacterDeviceImpl = (s) => s.isCharacterDevice(); +export const isDirectoryImpl = (s) => s.isDirectory(); +export const isFIFOImpl = (s) => s.isFIFO(); +export const isFileImpl = (s) => s.isFile(); +export const isSocketImpl = (s) => s.isSocket(); +export const isSymbolicLinkImpl = (s) => s.isSymbolicLink(); +export const devImpl = (s) => s.dev; +export const inodeImpl = (s) => s.ino; +export const modeImpl = (s) => s.mode; +export const nlinkImpl = (s) => s.nlink; +export const uidImpl = (s) => s.uid; +export const gidImpl = (s) => s.gid; +export const rdevImpl = (s) => s.rdev; +export const sizeImpl = (s) => s.size; +export const blkSizeImpl = (s) => s.blkSize; +export const blocksImpl = (s) => s.blocks; +export const accessedTimeMsImpl = (s) => s.atimeMs; +export const modifiedTimeMsImpl = (s) => s.mtimeMs; +export const statusChangedTimeMsImpl = (s) => s.ctimeMs; +export const birthtimeMsImpl = (s) => s.birthtimeMs; +export const accessedTimeImpl = (s) => s.atime; +export const modifiedTimeImpl = (s) => s.mtime; +export const statusChangedTimeImpl = (s) => s.ctime; +export const birthTimeImpl = (s) => s.birthtime; diff --git a/src/Node/FS/Stats.purs b/src/Node/FS/Stats.purs index d3fe67c..c31b01a 100644 --- a/src/Node/FS/Stats.purs +++ b/src/Node/FS/Stats.purs @@ -1,6 +1,5 @@ module Node.FS.Stats - ( Stats(..) - , StatsObj + ( Stats , isFile , isDirectory , isBlockDevice @@ -8,75 +7,186 @@ module Node.FS.Stats , isFIFO , isSocket , isSymbolicLink + , dev + , inode + , mode + , nlink + , uid + , gid + , rdev + , size + , blkSize + , blocks + , accessedTimeMs + , modifiedTimeMs + , statusChangedTimeMs + , birthtimeMs , accessedTime , modifiedTime , statusChangedTime + , birthTime ) where import Prelude import Data.DateTime (DateTime) -import Data.Function.Uncurried (Fn2, Fn0, runFn2) +import Data.Function.Uncurried (Fn1, runFn1) import Data.JSDate (JSDate, toDateTime) -import Data.Maybe (fromJust) -import Partial.Unsafe (unsafePartial) - -type StatsObj = - { dev :: Number - , mode :: Number - , nlink :: Number - , uid :: Number - , gid :: Number - , rdev :: Number - , ino :: Number - , size :: Number - , atime :: JSDate - , mtime :: JSDate - , ctime :: JSDate - , isFile :: Fn0 Boolean - , isDirectory :: Fn0 Boolean - , isBlockDevice :: Fn0 Boolean - , isCharacterDevice :: Fn0 Boolean - , isFIFO :: Fn0 Boolean - , isSocket :: Fn0 Boolean - } - --- | Stats wrapper to provide a usable interface to the underlying properties and methods. -data Stats = Stats StatsObj - -foreign import showStatsObj :: StatsObj -> String +import Data.Maybe (Maybe(..)) +import Data.Time.Duration (Milliseconds) +import Partial.Unsafe (unsafeCrashWith) -instance showStats :: Show Stats where - show (Stats o) = "Stats " <> showStatsObj o - -foreign import statsMethod :: Fn2 String StatsObj Boolean +foreign import data Stats :: Type -isFile :: Stats -> Boolean -isFile (Stats s) = runFn2 statsMethod "isFile" s +foreign import showStatsObj :: Stats -> String -isDirectory :: Stats -> Boolean -isDirectory (Stats s) = runFn2 statsMethod "isDirectory" s +instance showStats :: Show Stats where + show s = "Stats " <> showStatsObj s isBlockDevice :: Stats -> Boolean -isBlockDevice (Stats s) = runFn2 statsMethod "isBlockDevice" s +isBlockDevice s = runFn1 isBlockDeviceImpl s + +foreign import isBlockDeviceImpl :: Fn1 (Stats) (Boolean) isCharacterDevice :: Stats -> Boolean -isCharacterDevice (Stats s) = runFn2 statsMethod "isCharacterDevice" s +isCharacterDevice s = runFn1 isCharacterDeviceImpl s + +foreign import isCharacterDeviceImpl :: Fn1 (Stats) (Boolean) + +-- | Returns true if the object describes a file system directory. +-- | If the `fs.Stats`> object was obtained from `fs.lstat()`, +-- | this method will always return `false``. This is because `fs.lstat()` +-- | returns information about a symbolic link itself and not the path to which it resolves. +isDirectory :: Stats -> Boolean +isDirectory s = runFn1 isDirectoryImpl s + +foreign import isDirectoryImpl :: Fn1 (Stats) (Boolean) isFIFO :: Stats -> Boolean -isFIFO (Stats s) = runFn2 statsMethod "isFIFO" s +isFIFO s = runFn1 isFIFOImpl s + +foreign import isFIFOImpl :: Fn1 (Stats) (Boolean) + +isFile :: Stats -> Boolean +isFile s = runFn1 isFileImpl s + +foreign import isFileImpl :: Fn1 (Stats) (Boolean) isSocket :: Stats -> Boolean -isSocket (Stats s) = runFn2 statsMethod "isSocket" s +isSocket s = runFn1 isSocketImpl s + +foreign import isSocketImpl :: Fn1 (Stats) (Boolean) isSymbolicLink :: Stats -> Boolean -isSymbolicLink (Stats s) = runFn2 statsMethod "isSymbolicLink" s +isSymbolicLink s = runFn1 isSymbolicLinkImpl s + +foreign import isSymbolicLinkImpl :: Fn1 (Stats) (Boolean) + +-- | The numeric identifier of the device containing the file. +dev :: Stats -> Number +dev s = runFn1 devImpl s + +foreign import devImpl :: Fn1 (Stats) (Number) + +-- | The file system specific "Inode" number for the file. +inode :: Stats -> Number +inode s = runFn1 inodeImpl s + +foreign import inodeImpl :: Fn1 (Stats) (Number) + +-- | A bit-field describing the file type and mode. +mode :: Stats -> Number +mode s = runFn1 modeImpl s + +foreign import modeImpl :: Fn1 (Stats) (Number) + +-- | The number of hard-links that exist for the file. +nlink :: Stats -> Number +nlink s = runFn1 nlinkImpl s + +foreign import nlinkImpl :: Fn1 (Stats) (Number) + +-- | The numeric user identifier of the user that owns the file (POSIX). +uid :: Stats -> Number +uid s = runFn1 uidImpl s + +foreign import uidImpl :: Fn1 (Stats) (Number) + +-- | The numeric group identifier of the group that owns the file (POSIX). +gid :: Stats -> Number +gid s = runFn1 gidImpl s + +foreign import gidImpl :: Fn1 (Stats) (Number) + +-- | A numeric device identifier if the file represents a device. +rdev :: Stats -> Number +rdev s = runFn1 rdevImpl s + +foreign import rdevImpl :: Fn1 (Stats) (Number) + +-- | The size of the file in bytes. +-- | If the underlying file system does not support getting the size of the file, this will be 0. +size :: Stats -> Number +size s = runFn1 sizeImpl s + +foreign import sizeImpl :: Fn1 (Stats) (Number) + +-- | The file system block size for i/o operations. +blkSize :: Stats -> Number +blkSize s = runFn1 blkSizeImpl s + +foreign import blkSizeImpl :: Fn1 (Stats) (Number) + +-- | The number of blocks allocated for this file. +blocks :: Stats -> Number +blocks s = runFn1 blocksImpl s + +foreign import blocksImpl :: Fn1 (Stats) (Number) + +accessedTimeMs :: Stats -> Milliseconds +accessedTimeMs s = runFn1 accessedTimeMsImpl s + +foreign import accessedTimeMsImpl :: Fn1 (Stats) (Milliseconds) + +modifiedTimeMs :: Stats -> Milliseconds +modifiedTimeMs s = runFn1 modifiedTimeMsImpl s + +foreign import modifiedTimeMsImpl :: Fn1 (Stats) (Milliseconds) + +statusChangedTimeMs :: Stats -> Milliseconds +statusChangedTimeMs s = runFn1 statusChangedTimeMsImpl s + +foreign import statusChangedTimeMsImpl :: Fn1 (Stats) (Milliseconds) + +birthtimeMs :: Stats -> Milliseconds +birthtimeMs s = runFn1 birthtimeMsImpl s + +foreign import birthtimeMsImpl :: Fn1 (Stats) (Milliseconds) accessedTime :: Stats -> DateTime -accessedTime (Stats s) = unsafePartial $ fromJust (toDateTime s.atime) +accessedTime s = case toDateTime $ runFn1 accessedTimeImpl s of + Just d -> d + Nothing -> unsafeCrashWith $ "Impossible: `accessedTime` returned invalid DateTime value." + +foreign import accessedTimeImpl :: Fn1 (Stats) (JSDate) modifiedTime :: Stats -> DateTime -modifiedTime (Stats s) = unsafePartial $ fromJust (toDateTime s.mtime) +modifiedTime s = case toDateTime $ runFn1 modifiedTimeImpl s of + Just d -> d + Nothing -> unsafeCrashWith $ "Impossible: `modifiedTime` returned invalid DateTime value." + +foreign import modifiedTimeImpl :: Fn1 (Stats) (JSDate) statusChangedTime :: Stats -> DateTime -statusChangedTime (Stats s) = unsafePartial $ fromJust (toDateTime s.ctime) +statusChangedTime s = case toDateTime $ runFn1 statusChangedTimeImpl s of + Just d -> d + Nothing -> unsafeCrashWith $ "Impossible: `statusChangedTime` returned invalid DateTime value." + +foreign import statusChangedTimeImpl :: Fn1 (Stats) (JSDate) + +birthTime :: Stats -> DateTime +birthTime s = case toDateTime $ runFn1 birthTimeImpl s of + Just d -> d + Nothing -> unsafeCrashWith $ "Impossible: `birthTime` returned invalid DateTime value." + +foreign import birthTimeImpl :: Fn1 (Stats) (JSDate) diff --git a/src/Node/FS/Sync.purs b/src/Node/FS/Sync.purs index 9cadc4f..25fe268 100644 --- a/src/Node/FS/Sync.purs +++ b/src/Node/FS/Sync.purs @@ -58,7 +58,7 @@ import Node.Encoding (Encoding(..), encodingToNode) import Node.FS (FileDescriptor, ByteCount, FilePosition, BufferLength, BufferOffset, FileMode, SymlinkType, symlinkTypeToNode) import Node.FS.Constants (AccessMode, CopyMode, FileFlags, defaultAccessMode, defaultCopyMode, fileFlagsToNode) import Node.FS.Perms (Perms, permsToString, all, mkPerms) -import Node.FS.Stats (StatsObj, Stats(..)) +import Node.FS.Stats (Stats) import Node.Path (FilePath) access :: FilePath -> Effect (Maybe Error) @@ -90,8 +90,8 @@ foreign import renameSyncImpl :: EffectFn2 FilePath FilePath Unit foreign import truncateSyncImpl :: EffectFn2 FilePath Int Unit foreign import chownSyncImpl :: EffectFn3 FilePath Int Int Unit foreign import chmodSyncImpl :: EffectFn2 FilePath String Unit -foreign import statSyncImpl :: EffectFn1 FilePath StatsObj -foreign import lstatSyncImpl :: EffectFn1 FilePath StatsObj +foreign import statSyncImpl :: EffectFn1 FilePath Stats +foreign import lstatSyncImpl :: EffectFn1 FilePath Stats foreign import linkSyncImpl :: EffectFn2 FilePath FilePath Unit foreign import symlinkSyncImpl :: EffectFn3 FilePath FilePath String Unit foreign import readlinkSyncImpl :: EffectFn1 FilePath FilePath @@ -142,7 +142,7 @@ chmod file perms = runEffectFn2 chmodSyncImpl file (permsToString perms) stat :: FilePath -> Effect Stats -stat file = map Stats $ runEffectFn1 statSyncImpl file +stat file = runEffectFn1 statSyncImpl file -- | 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, @@ -150,7 +150,7 @@ stat file = map Stats $ runEffectFn1 statSyncImpl file lstat :: FilePath -> Effect Stats -lstat file = map Stats $ runEffectFn1 lstatSyncImpl file +lstat file = runEffectFn1 lstatSyncImpl file -- | Creates a link to an existing file. link diff --git a/test/Test.purs b/test/Test.purs index 914cd6b..16a591a 100644 --- a/test/Test.purs +++ b/test/Test.purs @@ -2,7 +2,7 @@ module Test where import Prelude -import Data.Either (Either(..), either, isRight) +import Data.Either (Either(..), either) import Data.Maybe (Maybe(..), isNothing) import Data.Traversable (for_, traverse) import Effect (Effect)