aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--stdlib/source/lux/world/fs.jvm.lux120
-rw-r--r--stdlib/test/test/lux/world/fs.lux162
2 files changed, 282 insertions, 0 deletions
diff --git a/stdlib/source/lux/world/fs.jvm.lux b/stdlib/source/lux/world/fs.jvm.lux
new file mode 100644
index 000000000..665dfaea1
--- /dev/null
+++ b/stdlib/source/lux/world/fs.jvm.lux
@@ -0,0 +1,120 @@
+(;module:
+ lux
+ (lux (control [monad #+ do]
+ ["ex" exception #+ exception:])
+ (concurrency ["P" promise]
+ ["T" task])
+ (data ["R" result]
+ (coll [array]))
+ (time ["i" instant]
+ ["d" duration])
+ (world [blob #+ Blob])
+ [io]
+ [host #+ jvm-import]))
+
+(exception: Could-Not-Read-All-Data)
+
+(type: #export File Text)
+
+(jvm-import #long java.io.File
+ (new [String])
+ (exists [] #io #try boolean)
+ (mkdir [] #io #try boolean)
+ (delete [] #io #try boolean)
+ (length [] #io #try long)
+ (listFiles [] #io #try (Array java.io.File))
+ (getAbsolutePath [] #io #try String)
+ (renameTo [java.io.File] #io #try boolean)
+ (isFile [] #io #try boolean)
+ (isDirectory [] #io #try boolean)
+ (lastModified [] #io #try long)
+ (setLastModified [long] #io #try boolean)
+ (canRead [] #io #try boolean)
+ (canWrite [] #io #try boolean)
+ (canExecute [] #io #try boolean))
+
+(jvm-import java.lang.AutoCloseable
+ (close [] #io #try void))
+
+(jvm-import java.io.OutputStream
+ (write [Byte-Array] #io #try void)
+ (flush [] #io #try void))
+
+(jvm-import java.io.FileOutputStream
+ (new [java.io.File boolean] #io #try))
+
+(jvm-import java.io.InputStream
+ (read [Byte-Array] #io #try int))
+
+(jvm-import java.io.FileInputStream
+ (new [java.io.File] #io #try))
+
+(do-template [<name> <flag>]
+ [(def: #export (<name> data file)
+ (-> Blob File (T;Task Unit))
+ (P;future (do (R;ResultT io;Monad<IO>)
+ [stream (FileOutputStream.new [(java.io.File.new file) <flag>])
+ _ (OutputStream.write [data] stream)
+ _ (OutputStream.flush [] stream)]
+ (AutoCloseable.close [] stream))))]
+
+ [append true]
+ [write false]
+ )
+
+(def: #export (read file)
+ (-> File (T;Task Blob))
+ (P;future (do (R;ResultT io;Monad<IO>)
+ [#let [file' (java.io.File.new file)]
+ size (java.io.File.length [] file')
+ #let [data (blob;create (int-to-nat size))]
+ stream (FileInputStream.new [file'])
+ bytes-read (InputStream.read [data] stream)
+ _ (AutoCloseable.close [] stream)]
+ (if (i.= size bytes-read)
+ (wrap data)
+ (io;io (ex;throw Could-Not-Read-All-Data file))))))
+
+(def: #export (size file)
+ (-> File (T;Task Nat))
+ (P;future (do (R;ResultT io;Monad<IO>)
+ [size (java.io.File.length [] (java.io.File.new file))]
+ (wrap (int-to-nat size)))))
+
+(def: #export (files dir)
+ (-> File (T;Task (List File)))
+ (P;future (do (R;ResultT io;Monad<IO>)
+ [files (java.io.File.listFiles [] (java.io.File.new dir))]
+ (monad;map @ (java.io.File.getAbsolutePath [])
+ (array;to-list files)))))
+
+(do-template [<name> <method>]
+ [(def: #export (<name> file)
+ (-> File (T;Task Bool))
+ (P;future (<method> [] (java.io.File.new file))))]
+
+ [exists? java.io.File.exists]
+ [make-dir java.io.File.mkdir]
+ [delete java.io.File.delete]
+ [file? java.io.File.isFile]
+ [directory? java.io.File.isDirectory]
+ [can-read? java.io.File.canRead]
+ [can-write? java.io.File.canWrite]
+ [can-execute? java.io.File.canExecute]
+ )
+
+(def: #export (move target source)
+ (-> File File (T;Task Bool))
+ (P;future (java.io.File.renameTo [(java.io.File.new target)]
+ (java.io.File.new source))))
+
+(def: #export (get-last-modified file)
+ (-> File (T;Task i;Instant))
+ (P;future (do (R;ResultT io;Monad<IO>)
+ [millis (java.io.File.lastModified [] (java.io.File.new file))]
+ (wrap (|> millis d;from-millis i;absolute)))))
+
+(def: #export (set-last-modified time file)
+ (-> i;Instant File (T;Task Bool))
+ (P;future (java.io.File.setLastModified [(|> time i;relative d;to-millis)]
+ (java.io.File.new file))))
diff --git a/stdlib/test/test/lux/world/fs.lux b/stdlib/test/test/lux/world/fs.lux
new file mode 100644
index 000000000..8289beac4
--- /dev/null
+++ b/stdlib/test/test/lux/world/fs.lux
@@ -0,0 +1,162 @@
+(;module:
+ lux
+ (lux [io]
+ (control [monad #+ do])
+ (concurrency ["P" promise]
+ ["T" task])
+ (data ["R" result]
+ [text]
+ text/format
+ [number])
+ (time ["i" instant]
+ ["d" duration])
+ (world ["@" fs]
+ [blob])
+ ["r" math/random])
+ lux/test
+ (.. ["_;" blob]))
+
+(def: truncate-millis
+ (|>. (i./ 1_000) (i.* 1_000)))
+
+(context: "File system."
+ #times +1
+ [file-size (|> r;nat (:: @ map (|>. (n.% +100) (n.max +10))))
+ dataL (_blob;blob file-size)
+ dataR (_blob;blob file-size)
+ code r;nat
+ last-modified (|> r;int (:: @ map (|>. (:: number;Number<Int> abs)
+ truncate-millis
+ d;from-millis
+ i;absolute)))]
+ ($_ seq
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +0 code)))]
+ result (do T;Monad<Task>
+ [pre (@;exists? file)
+ _ (@;write dataL file)
+ post (@;exists? file)
+ deleted? (@;delete file)
+ remains? (@;exists? file)]
+ (wrap (and (not pre) post
+ deleted? (not remains?))))]
+ (test "Can create/delete files."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +1 code)))]
+ result (do T;Monad<Task>
+ [_ (@;write dataL file)
+ output (@;read file)
+ _ (@;delete file)]
+ (wrap (:: blob;Eq<Blob> = dataL output)))]
+ (test "Can write/read files."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +2 code)))]
+ result (do T;Monad<Task>
+ [_ (@;write dataL file)
+ read-size (@;size file)
+ _ (@;delete file)]
+ (wrap (n.= file-size read-size)))]
+ (test "Can read file size."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +3 code)))]
+ result (do T;Monad<Task>
+ [_ (@;write dataL file)
+ _ (@;append dataR file)
+ output (@;read file)
+ read-size (@;size file)
+ _ (@;delete file)]
+ (wrap (and (n.= (n.* +2 file-size) read-size)
+ (:: blob;Eq<Blob> = dataL (R;assume (blob;slice +0 (n.dec file-size) output)))
+ (:: blob;Eq<Blob> = dataR (R;assume (blob;slice file-size (n.dec read-size) output))))))]
+ (test "Can append to files."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [dir (format "temp_dir_" (%n (n.+ +4 code)))]
+ result (do T;Monad<Task>
+ [pre (@;exists? dir)
+ _ (@;make-dir dir)
+ post (@;exists? dir)
+ deleted? (@;delete dir)
+ remains? (@;exists? dir)]
+ (wrap (and (not pre) post
+ deleted? (not remains?))))]
+ (test "Can create/delete directories."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +5 code)))
+ dir (format "temp_dir_" (%n (n.+ +5 code)))]
+ result (do T;Monad<Task>
+ [_ (@;write dataL file)
+ file-is-file (@;file? file)
+ file-is-directory (@;directory? file)
+ _ (@;delete file)
+ _ (@;make-dir dir)
+ directory-is-file (@;file? dir)
+ directory-is-directory (@;directory? dir)
+ _ (@;delete dir)]
+ (wrap (and file-is-file (not file-is-directory)
+ (not directory-is-file) directory-is-directory)))]
+ (test "Can differentiate files from directories."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +6 code)))
+ dir (format "temp_dir_" (%n (n.+ +6 code)))]
+ result (do T;Monad<Task>
+ [_ (@;make-dir dir)
+ #let [file' (format dir "/" file)]
+ _ (@;write dataL file')
+ read-size (@;size file')
+ deleted-file (@;delete file')
+ deleted-dir (@;delete dir)]
+ (wrap (and (n.= file-size read-size)
+ deleted-file
+ deleted-dir)))]
+ (test "Can create files inside of directories."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +7 code)))
+ dir (format "temp_dir_" (%n (n.+ +7 code)))]
+ result (do T;Monad<Task>
+ [_ (@;make-dir dir)
+ #let [file' (format dir "/" file)]
+ _ (@;write dataL file')
+ children (@;files dir)
+ _ (@;delete file')
+ _ (@;delete dir)]
+ (wrap (case children
+ (^ (list child))
+ (text;ends-with? file' child)
+
+ _
+ false)))]
+ (test "Can list files inside a directory."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file (format "temp_file_" (%n (n.+ +8 code)))]
+ result (do T;Monad<Task>
+ [_ (@;write dataL file)
+ was-modified? (@;set-last-modified last-modified file)
+ time-read (@;get-last-modified file)
+ _ (@;delete file)]
+ (wrap (and was-modified?
+ (:: i;Eq<Instant> = last-modified time-read))))]
+ (test "Can change the time of last modification."
+ (R;default false result)))
+ (do P;Monad<Promise>
+ [#let [file0 (format "temp_file_" (%n (n.+ +9 code)) "0")
+ file1 (format "temp_file_" (%n (n.+ +9 code)) "1")]
+ result (do T;Monad<Task>
+ [_ (@;write dataL file0)
+ pre (@;exists? file0)
+ moved? (@;move file1 file0)
+ post (@;exists? file0)
+ confirmed? (@;exists? file1)
+ deleted? (@;delete file1)]
+ (wrap (and pre moved? (not post)
+ confirmed? deleted?)))]
+ (test "Can move a file from one path to another."
+ (R;default false result)))
+ ))