Skip to content

Commit c4f638e

Browse files
authored
feat(stdlib): Add user-friendly file system module (#1966)
1 parent 1c70fb9 commit c4f638e

File tree

9 files changed

+2020
-2
lines changed

9 files changed

+2020
-2
lines changed

compiler/test/stdlib/fs.test.gr

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
module FsTest
2+
3+
from "fs" include Fs
4+
from "path" include Path
5+
from "bytes" include Bytes
6+
from "result" include Result
7+
from "list" include List
8+
9+
let path = Path.fromString
10+
let testPath = str => path("test/test-data/" ++ str)
11+
12+
// readFile
13+
assert Fs.Binary.readFile(testPath("foo.txt")) == Ok(b"foo, bar, & baz")
14+
assert Fs.Utf8.readFile(testPath("foo.txt")) == Ok("foo, bar, & baz")
15+
assert Fs.Binary.readFile(path("/test/test-data/foo.txt")) ==
16+
Ok(b"foo, bar, & baz")
17+
assert Fs.Binary.readFile(path("./test/test-data/foo.txt")) ==
18+
Ok(b"foo, bar, & baz")
19+
assert Fs.Binary.readFile(path("/test/test-data/../test-data/foo.txt")) ==
20+
Ok(b"foo, bar, & baz")
21+
assert Fs.Binary.readFile(
22+
baseDirPath=Some(testPath("test-dir")),
23+
path("file1.txt")
24+
) ==
25+
Ok(b"File 1")
26+
assert Fs.Binary.readFile(path("blahblah")) == Err(Fs.NoSuchFileOrDirectory)
27+
28+
// writeFile
29+
assert Fs.Binary.readFile(testPath("baz.txt")) == Err(Fs.NoSuchFileOrDirectory)
30+
assert Fs.Binary.writeFile(testPath("baz.txt"), b"some stuff") == Ok(void)
31+
assert Fs.Binary.readFile(testPath("baz.txt")) == Ok(b"some stuff")
32+
assert Fs.Binary.writeFile(sync=false, testPath("baz.txt"), b"some other stuff") ==
33+
Ok(void)
34+
assert Fs.Binary.readFile(sync=false, testPath("baz.txt")) ==
35+
Ok(b"some other stuff")
36+
37+
assert Fs.Utf8.writeFile(testPath("baz.txt"), "some other stuff 🌾") == Ok(void)
38+
assert Fs.Utf8.readFile(testPath("baz.txt")) == Ok("some other stuff 🌾")
39+
40+
assert Fs.Binary.writeFile(
41+
testPath("baz.txt"),
42+
b"\nmore stuff",
43+
writeMode=Fs.Append
44+
) ==
45+
Ok(void)
46+
assert Fs.Utf8.readFile(testPath("baz.txt")) ==
47+
Ok("some other stuff 🌾\nmore stuff")
48+
49+
assert Fs.Utf8.writeFile(
50+
testPath("baz.txt"),
51+
"\neven more stuff 😳",
52+
writeMode=Fs.Append,
53+
sync=false
54+
) ==
55+
Ok(void)
56+
assert Fs.Utf8.readFile(testPath("baz.txt")) ==
57+
Ok("some other stuff 🌾\nmore stuff\neven more stuff 😳")
58+
59+
assert Fs.Utf8.readFile(testPath("foobar.txt")) == Err(Fs.NoSuchFileOrDirectory)
60+
assert Fs.Utf8.writeFile(
61+
testPath("foobar.txt"),
62+
"new content",
63+
writeMode=Fs.Append
64+
) ==
65+
Ok(void)
66+
assert Fs.Utf8.readFile(testPath("foobar.txt")) == Ok("new content")
67+
68+
assert Fs.Binary.writeFile(
69+
baseDirPath=Some(testPath("test-dir")),
70+
path("in-directory.txt"),
71+
b"some stuff in a directory"
72+
) ==
73+
Ok(void)
74+
assert Fs.Binary.readFile(testPath("test-dir/in-directory.txt")) ==
75+
Ok(b"some stuff in a directory")
76+
77+
// stats
78+
let stats = Result.unwrap(Fs.stats(testPath("foo.txt")))
79+
assert stats.fileType == Fs.File
80+
assert stats.size == 15
81+
82+
assert Result.unwrap(Fs.stats(testPath("test-dir"))).fileType == Fs.Directory
83+
assert Result.unwrap(
84+
Fs.stats(baseDirPath=Some(testPath("test-dir")), path("link"))
85+
).fileType ==
86+
Fs.File
87+
assert Result.unwrap(
88+
Fs.stats(
89+
baseDirPath=Some(testPath("test-dir")),
90+
path("link"),
91+
followSymlink=false
92+
)
93+
).fileType ==
94+
Fs.SymbolicLink
95+
assert Fs.stats(testPath("blahblah")) == Err(Fs.NoSuchFileOrDirectory)
96+
97+
// exists
98+
assert Fs.exists(testPath("foo.txt"))
99+
assert !Fs.exists(testPath("blahblah"))
100+
assert !Fs.exists(path(".."))
101+
102+
// readDir
103+
use Fs.{ type DirectoryEntry }
104+
assert Result.map(
105+
x => List.sort(compare=(x, y) => compare(x.name, y.name), x),
106+
Fs.readDir(testPath("test-dir"))
107+
) ==
108+
Ok(
109+
[
110+
{ name: "dir", fileType: Fs.Directory },
111+
{ name: "file1.txt", fileType: Fs.File },
112+
{ name: "file2.txt", fileType: Fs.File },
113+
{ name: "file3.txt", fileType: Fs.File },
114+
{ name: "in-directory.txt", fileType: Fs.File },
115+
{ name: "link", fileType: Fs.SymbolicLink },
116+
],
117+
)
118+
assert Fs.readDir(baseDirPath=Some(testPath("test-dir")), path("dir")) ==
119+
Ok([{ name: "nested.txt", fileType: Fs.File }])
120+
assert Fs.readDir(path("test/nonexisting")) == Err(Fs.NoSuchFileOrDirectory)
121+
122+
// createDir
123+
assert !Fs.exists(testPath("dir2"))
124+
assert Fs.createDir(testPath("dir2")) == Ok(void)
125+
assert Fs.readDir(testPath("dir2")) == Ok([])
126+
127+
assert Fs.createDir(baseDirPath=Some(testPath("dir2")), path("inner")) ==
128+
Ok(void)
129+
assert Fs.readDir(testPath("dir2")) ==
130+
Ok([{ name: "inner", fileType: Fs.Directory }])
131+
132+
assert Fs.createDir(testPath("dir2")) == Err(Fs.FileExists)
133+
134+
// readLink
135+
assert Fs.readLink(testPath("test-dir/link")) == Ok(path("file1.txt"))
136+
assert Fs.readLink(baseDirPath=Some(testPath("test-dir")), path("link")) ==
137+
Ok(path("file1.txt"))
138+
assert Fs.readLink(testPath("blahblah")) == Err(Fs.NoSuchFileOrDirectory)
139+
assert Fs.readLink(testPath("foo.txt")) == Err(Fs.InvalidArgument)
140+
141+
// createSymlink
142+
assert !Fs.exists(testPath("symlink"))
143+
assert Fs.createSymlink(path("foo.txt"), testPath("symlink")) == Ok(void)
144+
assert Fs.readLink(testPath("symlink")) == Ok(path("foo.txt"))
145+
assert Result.unwrap(Fs.stats(testPath("symlink"), followSymlink=false)).fileType ==
146+
Fs.SymbolicLink
147+
assert Result.unwrap(Fs.stats(testPath("symlink"), followSymlink=true)).fileType ==
148+
Fs.File
149+
assert Fs.createSymlink(path("bar.txt"), testPath("symlink")) ==
150+
Err(Fs.FileExists)
151+
assert Fs.createSymlink(
152+
path("bar.txt"),
153+
targetBaseDirPath=Some(testPath("test-dir")),
154+
path("symlink")
155+
) ==
156+
Ok(void)
157+
assert Fs.readLink(testPath("test-dir/symlink")) == Ok(path("bar.txt"))
158+
159+
// copy
160+
assert Fs.copy(testPath("foo.txt"), testPath("foocopy.txt")) == Ok(void)
161+
assert Fs.Utf8.readFile(testPath("foocopy.txt")) == Ok("foo, bar, & baz")
162+
163+
assert Fs.copy(
164+
testPath("test-dir"),
165+
testPath("copied-dir"),
166+
copyMode=Fs.CopyRecursive
167+
) ==
168+
Ok(void)
169+
170+
assert Fs.readDir(testPath("copied-dir")) == Fs.readDir(testPath("test-dir"))
171+
assert Fs.Utf8.writeFile(testPath("test-dir/newfile.txt"), "New contents") ==
172+
Ok(void)
173+
assert Fs.copy(
174+
sourceBaseDirPath=Some(testPath("test-dir")),
175+
path("file1.txt"),
176+
targetBaseDirPath=Some(testPath("test-dir")),
177+
path("newfile.txt")
178+
) ==
179+
Ok(void)
180+
assert Fs.Utf8.readFile(testPath("test-dir/newfile.txt")) == Ok("File 1")
181+
182+
assert Fs.copy(
183+
testPath("test-dir"),
184+
testPath("copied-dir"),
185+
copyMode=Fs.CopyRecursive
186+
) ==
187+
Err(Fs.FileExists)
188+
189+
assert Fs.copy(
190+
testPath("test-dir/link"),
191+
testPath("linkcopy"),
192+
followSymlink=false
193+
) ==
194+
Ok(void)
195+
assert Fs.readLink(testPath("linkcopy")) == Ok(path("file1.txt"))
196+
197+
assert Fs.copy(
198+
testPath("test-dir/link"),
199+
testPath("contentscopy.txt"),
200+
followSymlink=true
201+
) ==
202+
Ok(void)
203+
assert Result.unwrap(
204+
Fs.stats(testPath("contentscopy.txt"), followSymlink=false)
205+
).fileType ==
206+
Fs.File
207+
208+
assert Fs.createSymlink(path("test-dir"), testPath("linktodir")) == Ok(void)
209+
assert Fs.copy(
210+
testPath("linktodir"),
211+
testPath("copied-link-to-dir"),
212+
copyMode=Fs.CopyRecursive,
213+
followSymlink=true
214+
) ==
215+
Ok(void)
216+
assert Result.unwrap(
217+
Fs.stats(testPath("copied-link-to-dir"), followSymlink=false)
218+
).fileType ==
219+
Fs.Directory
220+
221+
assert Fs.copy(
222+
testPath("linktodir"),
223+
testPath("copied-link"),
224+
copyMode=Fs.CopyRecursive,
225+
followSymlink=false
226+
) ==
227+
Ok(void)
228+
assert Result.unwrap(Fs.stats(testPath("copied-link"), followSymlink=false)).fileType ==
229+
Fs.SymbolicLink
230+
231+
// rename
232+
assert Fs.rename(testPath("foobar.txt"), testPath("boofar.txt")) == Ok(void)
233+
assert !Fs.exists(testPath("foobar.txt"))
234+
assert Fs.Utf8.readFile(testPath("boofar.txt")) == Ok("new content")
235+
236+
assert Fs.rename(testPath("dir2"), testPath("newdir")) == Ok(void)
237+
assert !Fs.exists(testPath("dir2"))
238+
assert Fs.readDir(testPath("newdir")) ==
239+
Ok([{ name: "inner", fileType: Fs.Directory }])
240+
241+
assert Fs.rename(
242+
sourceBaseDirPath=Some(testPath("test-dir")),
243+
path("in-directory.txt"),
244+
targetBaseDirPath=Some(testPath("test-dir")),
245+
path("renamed.txt")
246+
) ==
247+
Ok(void)
248+
assert !Fs.exists(testPath("test-dir/in-directory.txt"))
249+
assert Fs.Utf8.readFile(testPath("test-dir/renamed.txt")) ==
250+
Ok("some stuff in a directory")
251+
252+
assert Fs.rename(
253+
sourceBaseDirPath=Some(testPath("test-dir")),
254+
path("renamed.txt"),
255+
testPath("boofar.txt")
256+
) ==
257+
Ok(void)
258+
assert Fs.Utf8.readFile(testPath("boofar.txt")) ==
259+
Ok("some stuff in a directory")
260+
261+
// remove
262+
assert Fs.remove(testPath("baz.txt")) == Ok(void)
263+
assert Fs.remove(testPath("newdir")) == Err(Fs.IsADirectory)
264+
assert Fs.createDir(testPath("newdir/innerdir")) == Ok(void)
265+
assert Fs.remove(testPath("newdir"), removeMode=Fs.RemoveEmptyDirectory) ==
266+
Err(Fs.DirectoryNotEmpty)
267+
assert Fs.remove(
268+
baseDirPath=Some(testPath("newdir")),
269+
path("innerdir"),
270+
removeMode=Fs.RemoveEmptyDirectory
271+
) ==
272+
Ok(void)
273+
assert Fs.createDir(testPath("newdir/innerdir")) == Ok(void)
274+
assert Fs.Utf8.writeFile(testPath("newdir/innerdir/file.txt"), "content") ==
275+
Ok(void)
276+
assert Fs.remove(testPath("newdir"), removeMode=Fs.RemoveRecursive) == Ok(void)
277+
assert !Fs.exists(testPath("newdir"))
278+
279+
// clean up created files through tests
280+
assert Fs.remove(testPath("boofar.txt")) == Ok(void)
281+
assert Fs.remove(testPath("symlink")) == Ok(void)
282+
assert Fs.remove(testPath("linkcopy")) == Ok(void)
283+
assert Fs.remove(testPath("foocopy.txt")) == Ok(void)
284+
assert Fs.remove(testPath("contentscopy.txt")) == Ok(void)
285+
assert Fs.remove(testPath("copied-link")) == Ok(void)
286+
assert Fs.remove(testPath("linktodir")) == Ok(void)
287+
assert Fs.remove(testPath("test-dir/symlink")) == Ok(void)
288+
assert Fs.remove(testPath("test-dir/newfile.txt")) == Ok(void)
289+
assert Fs.remove(testPath("copied-dir"), removeMode=Fs.RemoveRecursive) ==
290+
Ok(void)
291+
assert Fs.remove(testPath("copied-link-to-dir"), removeMode=Fs.RemoveRecursive) ==
292+
Ok(void)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In a directory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
File 1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
File 2
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
File 3
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./file1.txt

0 commit comments

Comments
 (0)