Skip to content

Commit 0020e02

Browse files
authoredJul 14, 2023
Reimplement bindings to Stats object (#76)
1 parent 8f694ba commit 0020e02

File tree

6 files changed

+206
-60
lines changed

6 files changed

+206
-60
lines changed
 

‎CHANGELOG.md

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ Breaking changes:
88

99
New features:
1010
- Integrate `node-fs-aff` into library (#75 by @JordanMartinez)
11+
- Remove `StatsObj` and reimplement bindings to `Stats` object (#76 by @JordanMartinez)
12+
13+
Previously, one could write the following to get a value on the `Stats` object:
14+
```purs
15+
getGid :: Stats -> Number
16+
getGid (Stats obj) = obj.gid
17+
```
18+
19+
This record interface was removed as the underlying value is not a record
20+
that can be copied and modified as such. Now, one must call the corresponding function:
21+
```purs
22+
getGid :: Stats -> Number
23+
getGid s = Stats.gid s
24+
```
1125

1226
Bugfixes:
1327

‎src/Node/FS/Async.purs

+5-5
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import Node.Encoding (Encoding(..), encodingToNode)
5757
import Node.FS (FileDescriptor, ByteCount, FilePosition, BufferLength, BufferOffset, FileMode, SymlinkType, symlinkTypeToNode)
5858
import Node.FS.Constants (FileFlags, fileFlagsToNode, AccessMode, CopyMode, defaultAccessMode, defaultCopyMode)
5959
import Node.FS.Perms (Perms, permsToString, all, mkPerms)
60-
import Node.FS.Stats (StatsObj, Stats(..))
60+
import Node.FS.Stats (Stats)
6161
import Node.Path (FilePath)
6262

6363
type JSCallback a = EffectFn2 (Nullable Error) a Unit
@@ -99,8 +99,8 @@ foreign import renameImpl :: EffectFn3 FilePath FilePath (JSCallback Unit) Unit
9999
foreign import truncateImpl :: EffectFn3 FilePath Int (JSCallback Unit) Unit
100100
foreign import chownImpl :: EffectFn4 FilePath Int Int (JSCallback Unit) Unit
101101
foreign import chmodImpl :: EffectFn3 FilePath String (JSCallback Unit) Unit
102-
foreign import statImpl :: EffectFn2 FilePath (JSCallback StatsObj) Unit
103-
foreign import lstatImpl :: EffectFn2 FilePath (JSCallback StatsObj) Unit
102+
foreign import statImpl :: EffectFn2 FilePath (JSCallback Stats) Unit
103+
foreign import lstatImpl :: EffectFn2 FilePath (JSCallback Stats) Unit
104104
foreign import linkImpl :: EffectFn3 FilePath FilePath (JSCallback Unit) Unit
105105
foreign import symlinkImpl :: EffectFn4 FilePath FilePath String (JSCallback Unit) Unit
106106
foreign import readlinkImpl :: EffectFn2 FilePath (JSCallback FilePath) Unit
@@ -157,7 +157,7 @@ stat
157157
:: FilePath
158158
-> Callback Stats
159159
-> Effect Unit
160-
stat file cb = runEffectFn2 statImpl file (handleCallback $ cb <<< map Stats)
160+
stat file cb = runEffectFn2 statImpl file (handleCallback $ cb)
161161

162162
-- | Gets file or symlink statistics. `lstat` is identical to `stat`, except
163163
-- | that if the `FilePath` is a symbolic link, then the link itself is stat-ed,
@@ -166,7 +166,7 @@ lstat
166166
:: FilePath
167167
-> Callback Stats
168168
-> Effect Unit
169-
lstat file cb = runEffectFn2 lstatImpl file (handleCallback $ cb <<< map Stats)
169+
lstat file cb = runEffectFn2 lstatImpl file (handleCallback $ cb)
170170

171171
-- | Creates a link to an existing file.
172172
link

‎src/Node/FS/Stats.js

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
export { inspect as showStatsObj } from "util";
22

3-
export function statsMethod(m, s) {
4-
return s[m]();
5-
}
3+
export const isBlockDeviceImpl = (s) => s.isBlockDevice();
4+
export const isCharacterDeviceImpl = (s) => s.isCharacterDevice();
5+
export const isDirectoryImpl = (s) => s.isDirectory();
6+
export const isFIFOImpl = (s) => s.isFIFO();
7+
export const isFileImpl = (s) => s.isFile();
8+
export const isSocketImpl = (s) => s.isSocket();
9+
export const isSymbolicLinkImpl = (s) => s.isSymbolicLink();
10+
export const devImpl = (s) => s.dev;
11+
export const inodeImpl = (s) => s.ino;
12+
export const modeImpl = (s) => s.mode;
13+
export const nlinkImpl = (s) => s.nlink;
14+
export const uidImpl = (s) => s.uid;
15+
export const gidImpl = (s) => s.gid;
16+
export const rdevImpl = (s) => s.rdev;
17+
export const sizeImpl = (s) => s.size;
18+
export const blkSizeImpl = (s) => s.blkSize;
19+
export const blocksImpl = (s) => s.blocks;
20+
export const accessedTimeMsImpl = (s) => s.atimeMs;
21+
export const modifiedTimeMsImpl = (s) => s.mtimeMs;
22+
export const statusChangedTimeMsImpl = (s) => s.ctimeMs;
23+
export const birthtimeMsImpl = (s) => s.birthtimeMs;
24+
export const accessedTimeImpl = (s) => s.atime;
25+
export const modifiedTimeImpl = (s) => s.mtime;
26+
export const statusChangedTimeImpl = (s) => s.ctime;
27+
export const birthTimeImpl = (s) => s.birthtime;

‎src/Node/FS/Stats.purs

+156-46
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,192 @@
11
module Node.FS.Stats
2-
( Stats(..)
3-
, StatsObj
2+
( Stats
43
, isFile
54
, isDirectory
65
, isBlockDevice
76
, isCharacterDevice
87
, isFIFO
98
, isSocket
109
, isSymbolicLink
10+
, dev
11+
, inode
12+
, mode
13+
, nlink
14+
, uid
15+
, gid
16+
, rdev
17+
, size
18+
, blkSize
19+
, blocks
20+
, accessedTimeMs
21+
, modifiedTimeMs
22+
, statusChangedTimeMs
23+
, birthtimeMs
1124
, accessedTime
1225
, modifiedTime
1326
, statusChangedTime
27+
, birthTime
1428
) where
1529

1630
import Prelude
1731

1832
import Data.DateTime (DateTime)
19-
import Data.Function.Uncurried (Fn2, Fn0, runFn2)
33+
import Data.Function.Uncurried (Fn1, runFn1)
2034
import Data.JSDate (JSDate, toDateTime)
21-
import Data.Maybe (fromJust)
22-
import Partial.Unsafe (unsafePartial)
23-
24-
type StatsObj =
25-
{ dev :: Number
26-
, mode :: Number
27-
, nlink :: Number
28-
, uid :: Number
29-
, gid :: Number
30-
, rdev :: Number
31-
, ino :: Number
32-
, size :: Number
33-
, atime :: JSDate
34-
, mtime :: JSDate
35-
, ctime :: JSDate
36-
, isFile :: Fn0 Boolean
37-
, isDirectory :: Fn0 Boolean
38-
, isBlockDevice :: Fn0 Boolean
39-
, isCharacterDevice :: Fn0 Boolean
40-
, isFIFO :: Fn0 Boolean
41-
, isSocket :: Fn0 Boolean
42-
}
43-
44-
-- | Stats wrapper to provide a usable interface to the underlying properties and methods.
45-
data Stats = Stats StatsObj
46-
47-
foreign import showStatsObj :: StatsObj -> String
35+
import Data.Maybe (Maybe(..))
36+
import Data.Time.Duration (Milliseconds)
37+
import Partial.Unsafe (unsafeCrashWith)
4838

49-
instance showStats :: Show Stats where
50-
show (Stats o) = "Stats " <> showStatsObj o
51-
52-
foreign import statsMethod :: Fn2 String StatsObj Boolean
39+
foreign import data Stats :: Type
5340

54-
isFile :: Stats -> Boolean
55-
isFile (Stats s) = runFn2 statsMethod "isFile" s
41+
foreign import showStatsObj :: Stats -> String
5642

57-
isDirectory :: Stats -> Boolean
58-
isDirectory (Stats s) = runFn2 statsMethod "isDirectory" s
43+
instance showStats :: Show Stats where
44+
show s = "Stats " <> showStatsObj s
5945

6046
isBlockDevice :: Stats -> Boolean
61-
isBlockDevice (Stats s) = runFn2 statsMethod "isBlockDevice" s
47+
isBlockDevice s = runFn1 isBlockDeviceImpl s
48+
49+
foreign import isBlockDeviceImpl :: Fn1 (Stats) (Boolean)
6250

6351
isCharacterDevice :: Stats -> Boolean
64-
isCharacterDevice (Stats s) = runFn2 statsMethod "isCharacterDevice" s
52+
isCharacterDevice s = runFn1 isCharacterDeviceImpl s
53+
54+
foreign import isCharacterDeviceImpl :: Fn1 (Stats) (Boolean)
55+
56+
-- | Returns true if the <fs.Stats> object describes a file system directory.
57+
-- | If the `fs.Stats`> object was obtained from `fs.lstat()`,
58+
-- | this method will always return `false``. This is because `fs.lstat()`
59+
-- | returns information about a symbolic link itself and not the path to which it resolves.
60+
isDirectory :: Stats -> Boolean
61+
isDirectory s = runFn1 isDirectoryImpl s
62+
63+
foreign import isDirectoryImpl :: Fn1 (Stats) (Boolean)
6564

6665
isFIFO :: Stats -> Boolean
67-
isFIFO (Stats s) = runFn2 statsMethod "isFIFO" s
66+
isFIFO s = runFn1 isFIFOImpl s
67+
68+
foreign import isFIFOImpl :: Fn1 (Stats) (Boolean)
69+
70+
isFile :: Stats -> Boolean
71+
isFile s = runFn1 isFileImpl s
72+
73+
foreign import isFileImpl :: Fn1 (Stats) (Boolean)
6874

6975
isSocket :: Stats -> Boolean
70-
isSocket (Stats s) = runFn2 statsMethod "isSocket" s
76+
isSocket s = runFn1 isSocketImpl s
77+
78+
foreign import isSocketImpl :: Fn1 (Stats) (Boolean)
7179

7280
isSymbolicLink :: Stats -> Boolean
73-
isSymbolicLink (Stats s) = runFn2 statsMethod "isSymbolicLink" s
81+
isSymbolicLink s = runFn1 isSymbolicLinkImpl s
82+
83+
foreign import isSymbolicLinkImpl :: Fn1 (Stats) (Boolean)
84+
85+
-- | The numeric identifier of the device containing the file.
86+
dev :: Stats -> Number
87+
dev s = runFn1 devImpl s
88+
89+
foreign import devImpl :: Fn1 (Stats) (Number)
90+
91+
-- | The file system specific "Inode" number for the file.
92+
inode :: Stats -> Number
93+
inode s = runFn1 inodeImpl s
94+
95+
foreign import inodeImpl :: Fn1 (Stats) (Number)
96+
97+
-- | A bit-field describing the file type and mode.
98+
mode :: Stats -> Number
99+
mode s = runFn1 modeImpl s
100+
101+
foreign import modeImpl :: Fn1 (Stats) (Number)
102+
103+
-- | The number of hard-links that exist for the file.
104+
nlink :: Stats -> Number
105+
nlink s = runFn1 nlinkImpl s
106+
107+
foreign import nlinkImpl :: Fn1 (Stats) (Number)
108+
109+
-- | The numeric user identifier of the user that owns the file (POSIX).
110+
uid :: Stats -> Number
111+
uid s = runFn1 uidImpl s
112+
113+
foreign import uidImpl :: Fn1 (Stats) (Number)
114+
115+
-- | The numeric group identifier of the group that owns the file (POSIX).
116+
gid :: Stats -> Number
117+
gid s = runFn1 gidImpl s
118+
119+
foreign import gidImpl :: Fn1 (Stats) (Number)
120+
121+
-- | A numeric device identifier if the file represents a device.
122+
rdev :: Stats -> Number
123+
rdev s = runFn1 rdevImpl s
124+
125+
foreign import rdevImpl :: Fn1 (Stats) (Number)
126+
127+
-- | The size of the file in bytes.
128+
-- | If the underlying file system does not support getting the size of the file, this will be 0.
129+
size :: Stats -> Number
130+
size s = runFn1 sizeImpl s
131+
132+
foreign import sizeImpl :: Fn1 (Stats) (Number)
133+
134+
-- | The file system block size for i/o operations.
135+
blkSize :: Stats -> Number
136+
blkSize s = runFn1 blkSizeImpl s
137+
138+
foreign import blkSizeImpl :: Fn1 (Stats) (Number)
139+
140+
-- | The number of blocks allocated for this file.
141+
blocks :: Stats -> Number
142+
blocks s = runFn1 blocksImpl s
143+
144+
foreign import blocksImpl :: Fn1 (Stats) (Number)
145+
146+
accessedTimeMs :: Stats -> Milliseconds
147+
accessedTimeMs s = runFn1 accessedTimeMsImpl s
148+
149+
foreign import accessedTimeMsImpl :: Fn1 (Stats) (Milliseconds)
150+
151+
modifiedTimeMs :: Stats -> Milliseconds
152+
modifiedTimeMs s = runFn1 modifiedTimeMsImpl s
153+
154+
foreign import modifiedTimeMsImpl :: Fn1 (Stats) (Milliseconds)
155+
156+
statusChangedTimeMs :: Stats -> Milliseconds
157+
statusChangedTimeMs s = runFn1 statusChangedTimeMsImpl s
158+
159+
foreign import statusChangedTimeMsImpl :: Fn1 (Stats) (Milliseconds)
160+
161+
birthtimeMs :: Stats -> Milliseconds
162+
birthtimeMs s = runFn1 birthtimeMsImpl s
163+
164+
foreign import birthtimeMsImpl :: Fn1 (Stats) (Milliseconds)
74165

75166
accessedTime :: Stats -> DateTime
76-
accessedTime (Stats s) = unsafePartial $ fromJust (toDateTime s.atime)
167+
accessedTime s = case toDateTime $ runFn1 accessedTimeImpl s of
168+
Just d -> d
169+
Nothing -> unsafeCrashWith $ "Impossible: `accessedTime` returned invalid DateTime value."
170+
171+
foreign import accessedTimeImpl :: Fn1 (Stats) (JSDate)
77172

78173
modifiedTime :: Stats -> DateTime
79-
modifiedTime (Stats s) = unsafePartial $ fromJust (toDateTime s.mtime)
174+
modifiedTime s = case toDateTime $ runFn1 modifiedTimeImpl s of
175+
Just d -> d
176+
Nothing -> unsafeCrashWith $ "Impossible: `modifiedTime` returned invalid DateTime value."
177+
178+
foreign import modifiedTimeImpl :: Fn1 (Stats) (JSDate)
80179

81180
statusChangedTime :: Stats -> DateTime
82-
statusChangedTime (Stats s) = unsafePartial $ fromJust (toDateTime s.ctime)
181+
statusChangedTime s = case toDateTime $ runFn1 statusChangedTimeImpl s of
182+
Just d -> d
183+
Nothing -> unsafeCrashWith $ "Impossible: `statusChangedTime` returned invalid DateTime value."
184+
185+
foreign import statusChangedTimeImpl :: Fn1 (Stats) (JSDate)
186+
187+
birthTime :: Stats -> DateTime
188+
birthTime s = case toDateTime $ runFn1 birthTimeImpl s of
189+
Just d -> d
190+
Nothing -> unsafeCrashWith $ "Impossible: `birthTime` returned invalid DateTime value."
191+
192+
foreign import birthTimeImpl :: Fn1 (Stats) (JSDate)

‎src/Node/FS/Sync.purs

+5-5
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import Node.Encoding (Encoding(..), encodingToNode)
5858
import Node.FS (FileDescriptor, ByteCount, FilePosition, BufferLength, BufferOffset, FileMode, SymlinkType, symlinkTypeToNode)
5959
import Node.FS.Constants (AccessMode, CopyMode, FileFlags, defaultAccessMode, defaultCopyMode, fileFlagsToNode)
6060
import Node.FS.Perms (Perms, permsToString, all, mkPerms)
61-
import Node.FS.Stats (StatsObj, Stats(..))
61+
import Node.FS.Stats (Stats)
6262
import Node.Path (FilePath)
6363

6464
access :: FilePath -> Effect (Maybe Error)
@@ -90,8 +90,8 @@ foreign import renameSyncImpl :: EffectFn2 FilePath FilePath Unit
9090
foreign import truncateSyncImpl :: EffectFn2 FilePath Int Unit
9191
foreign import chownSyncImpl :: EffectFn3 FilePath Int Int Unit
9292
foreign import chmodSyncImpl :: EffectFn2 FilePath String Unit
93-
foreign import statSyncImpl :: EffectFn1 FilePath StatsObj
94-
foreign import lstatSyncImpl :: EffectFn1 FilePath StatsObj
93+
foreign import statSyncImpl :: EffectFn1 FilePath Stats
94+
foreign import lstatSyncImpl :: EffectFn1 FilePath Stats
9595
foreign import linkSyncImpl :: EffectFn2 FilePath FilePath Unit
9696
foreign import symlinkSyncImpl :: EffectFn3 FilePath FilePath String Unit
9797
foreign import readlinkSyncImpl :: EffectFn1 FilePath FilePath
@@ -142,15 +142,15 @@ chmod file perms = runEffectFn2 chmodSyncImpl file (permsToString perms)
142142
stat
143143
:: FilePath
144144
-> Effect Stats
145-
stat file = map Stats $ runEffectFn1 statSyncImpl file
145+
stat file = runEffectFn1 statSyncImpl file
146146

147147
-- | Gets file or symlink statistics. `lstat` is identical to `stat`, except
148148
-- | that if the `FilePath` is a symbolic link, then the link itself is stat-ed,
149149
-- | not the file that it refers to.
150150
lstat
151151
:: FilePath
152152
-> Effect Stats
153-
lstat file = map Stats $ runEffectFn1 lstatSyncImpl file
153+
lstat file = runEffectFn1 lstatSyncImpl file
154154

155155
-- | Creates a link to an existing file.
156156
link

‎test/Test.purs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Test where
22

33
import Prelude
44

5-
import Data.Either (Either(..), either, isRight)
5+
import Data.Either (Either(..), either)
66
import Data.Maybe (Maybe(..), isNothing)
77
import Data.Traversable (for_, traverse)
88
import Effect (Effect)

0 commit comments

Comments
 (0)
Please sign in to comment.