|
| 1 | +(import lua/basic (type#)) |
| 2 | +(import util (log!)) |
| 3 | + |
| 4 | +(defun access-tree! (inode path contents) :hidden |
| 5 | + (log! (.. "path: " path)) |
| 6 | + (if (= path "") |
| 7 | + inode |
| 8 | + (let* [(path-parts (string/split path "%/")) |
| 9 | + (child-inode (.> inode (car path-parts)))] |
| 10 | + (if (> (n path-parts) 1) |
| 11 | + (if (= (type# child-inode) "table") |
| 12 | + (access-tree! inode (string/concat (cdr path-parts) "/") contents) |
| 13 | + (error! "No such file or directory")) |
| 14 | + (if (/= (type# contents) "nil") |
| 15 | + (.<! inode (car path-parts) (or contents nil)) |
| 16 | + (if (/= (type# child-inode) "nil") |
| 17 | + child-inode |
| 18 | + (error! "No such file or directory"))))))) |
| 19 | + |
| 20 | +(defun create-handle (methods) :hidden |
| 21 | + (let* [(handle {}) |
| 22 | + (closed false) |
| 23 | + (handle-methods |
| 24 | + (merge methods |
| 25 | + { :close (lambda () (set! closed true)) })) ] |
| 26 | + (do [(method-name (keys handle-methods))] |
| 27 | + (.<! handle method-name |
| 28 | + (lambda (&args) |
| 29 | + (if (not closed) |
| 30 | + ((.> handle-methods method-name) (splice args)) |
| 31 | + (error! "Attempt to use a closed file"))))) |
| 32 | + handle)) |
| 33 | + |
| 34 | +(defun open-read-file! (fs-tree path binary) :hidden |
| 35 | + (with (inode (access-tree! fs-tree path)) |
| 36 | + (if (/= (type# inode) "string") |
| 37 | + (error! "Could not open file for reading") |
| 38 | + (if binary |
| 39 | + (with (index 1) |
| 40 | + (create-handle |
| 41 | + { :read (lambda () |
| 42 | + (with (result (string/byte (string/sub inode index index))) |
| 43 | + (inc! index) |
| 44 | + result)) })) |
| 45 | + (with (left-contents inode) |
| 46 | + (create-handle |
| 47 | + { :readLine (lambda () |
| 48 | + (if left-contents |
| 49 | + (with (lines (string/split left-contents "\n")) |
| 50 | + (if (and (> (n lines) 1) (or (/= (n lines) 2) (/= (cadr lines) ""))) |
| 51 | + (set! left-contents (string/concat (cdr lines) "\n")) |
| 52 | + (set! left-contents nil)) |
| 53 | + (car lines)) |
| 54 | + nil)) |
| 55 | + :readAll (lambda () |
| 56 | + (with (result (or left-contents "")) |
| 57 | + (set! left-contents nil) |
| 58 | + result)) })))))) |
| 59 | + |
| 60 | +(defun open-write-file! (fs-tree path append binary) :hidden |
| 61 | + (let* [(contents (if append |
| 62 | + (access-tree! fs-tree path) |
| 63 | + "")) |
| 64 | + (write-contents! (lambda () |
| 65 | + (access-tree! fs-tree path contents)))] |
| 66 | + (if (/= (type# contents) "string") |
| 67 | + (error! "Could not open file for writing") |
| 68 | + (progn |
| 69 | + (access-tree! fs-tree path contents) |
| 70 | + ; TODO: Optimise this so it doesn't traverse the tree on every write. |
| 71 | + (if binary |
| 72 | + (create-handle |
| 73 | + { :write (lambda (b) |
| 74 | + (set! contents (.. contents (string/char b))) |
| 75 | + (write-contents!)) }) |
| 76 | + (create-handle |
| 77 | + { :write (lambda (str) |
| 78 | + (set! contents (.. contents str)) |
| 79 | + (write-contents!)) |
| 80 | + :writeLine (lambda (str) |
| 81 | + (set! contents (.. contents str "\n")) |
| 82 | + (write-contents!)) })))))) |
| 83 | + |
| 84 | +(defun open-file! (fs-tree path mode) :hidden |
| 85 | + (log! (.. "open file " path " mode " mode)) |
| 86 | + (let* [(f-mode (string/sub mode 1 1)) |
| 87 | + (binary (= (string/sub mode 2 2) "b")) |
| 88 | + ((success handle) |
| 89 | + (pcall (lambda () |
| 90 | + (if (= f-mode "r") |
| 91 | + (open-read-file! fs-tree path binary) |
| 92 | + (open-write-file! fs-tree path (= f-mode "a") binary)))))] |
| 93 | + (if success |
| 94 | + handle |
| 95 | + (splice (list nil handle))))) |
| 96 | + |
| 97 | + |
| 98 | +(defun create (file) |
| 99 | + (let* [(fs-tree {})] |
| 100 | + { :list (lambda (path) |
| 101 | + (with (inode (access-tree! fs-tree path)) |
| 102 | + (if (= (type# inode) "table") |
| 103 | + (list->struct (keys inode)) |
| 104 | + (error! "Not a directory")))) |
| 105 | + :exists (lambda (path) |
| 106 | + (with ((ok inode) (pcall (lambda () (access-tree! fs-tree path)))) |
| 107 | + ok)) |
| 108 | + :isDir (lambda (path) |
| 109 | + (with ((ok inode) (pcall (lambda () (access-tree! fs-tree path)))) |
| 110 | + (and ok |
| 111 | + (= (type# inode) "table")))) |
| 112 | + :isReadOnly (const false) |
| 113 | + :getSize (const 0) |
| 114 | + :getFreeSpace (const 1000000000) |
| 115 | + :makeDir (lambda (path) |
| 116 | + (access-tree! fs-tree path {}) |
| 117 | + (log! (.. "tree: " (pretty fs-tree)))) |
| 118 | + :move (const nil) |
| 119 | + :copy (const nil) |
| 120 | + :delete (lambda (path) (access-tree! fs-tree path false)) |
| 121 | + :open (cut open-file! fs-tree <> <>) })) |
| 122 | + |
| 123 | + |
0 commit comments