mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
97 lines
3.6 KiB
Text
97 lines
3.6 KiB
Text
interface File
|
|
exposes [ FileReadErr, FileOpenErr, FileWriteErr, DirReadErr, readUtf8, writeUtf8 ]
|
|
imports [ Task.{ Task }, fx.Effect.{ after }, Path ]
|
|
|
|
# TODO FIXME should be able to import this as Path.{ Path }, but there's a bug.
|
|
Path : Path.Path
|
|
|
|
# These various file errors come from the POSIX errno values - see
|
|
# http://www.virtsync.com/c-error-codes-include-errno for the actual codes, and
|
|
# https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html for documentation
|
|
#
|
|
# The goal of this design is:
|
|
# * Whenever a function returns a `Task`, that task's error type represents all the errors that could happen.
|
|
# * The errors are union-friendly; if I run a task that reads, and then another that writes, I should get all the read *and* write errors.
|
|
# * To make the errors friendlier to chaining, they should always include the `Path` of the attempted operation. This way it's possible to tell which one failed after the fact.
|
|
|
|
|
|
## These errors can happen when opening a file, before attempting to read from
|
|
## it or write to it. The #FileReadErr and #FileWriteErr tag unions begin with
|
|
## these tags and then add more specific ones.
|
|
FileOpenErr a :
|
|
[
|
|
FileNotFound Path,
|
|
PermissionDenied Path,
|
|
SymLinkLoop Path,
|
|
TooManyOpenFiles Path,
|
|
IoError Path,
|
|
UnknownError I64 Path,
|
|
]a
|
|
|
|
## Errors when attempting to read a non-directory file.
|
|
FileReadErr a :
|
|
FileOpenErr
|
|
[
|
|
FileWasDir Path,
|
|
InvalidSeek Path,
|
|
IllegalByteSequence Path,
|
|
FileBusy Path,
|
|
]a
|
|
|
|
## Errors when attempting to read a directory.
|
|
DirReadErr a :
|
|
FileOpenErr
|
|
[
|
|
FileWasNotDir Path,
|
|
]a
|
|
|
|
## Errors when attempting to write a non-directory file.
|
|
FileWriteErr a :
|
|
FileOpenErr
|
|
[
|
|
FileWasDir Path,
|
|
ReadOnlyFileSystem Path,
|
|
]a
|
|
|
|
|
|
## Read a file's raw bytes
|
|
#readBytes : Path -> Task (List U8) (FileReadErr *)
|
|
#readBytes = \path ->
|
|
# Effect.readBytes (Path.toStr path)
|
|
|
|
## Read a file's bytes and interpret them as UTF-8 encoded text.
|
|
readUtf8 : Path -> Task.Task Str (FileReadErr [ BadUtf8 Str.Utf8ByteProblem Nat ]*)
|
|
readUtf8 = \path ->
|
|
Effect.map (Effect.readAllUtf8 (Path.toStr path)) \answer ->
|
|
# errno values - see
|
|
# https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
|
|
when answer.errno is
|
|
0 ->
|
|
when Str.fromUtf8 answer.bytes is
|
|
Ok str -> Ok str
|
|
Err (BadUtf8 problem index) -> Err (BadUtf8 problem index)
|
|
1 -> Err (PermissionDenied path)
|
|
2 -> Err (FileNotFound path)
|
|
19 -> Err (FileWasDir path)
|
|
# TODO handle other errno scenarios that could come up
|
|
_ -> Err (UnknownError answer.errno path)
|
|
|
|
writeUtf8 : Path, Str -> Task.Task {} (FileWriteErr [ BadThing ]*)
|
|
writeUtf8 = \path, data ->
|
|
path
|
|
|> Path.toStr
|
|
|> Effect.writeAllUtf8 data
|
|
|> Effect.map \res ->
|
|
when res.errno is
|
|
0 -> Ok {}
|
|
_ -> Err BadThing
|
|
|
|
## Read a file's bytes, one chunk at a time, and use it to build up a state.
|
|
##
|
|
## After each chunk is read, it gets passed to a callback which builds up a
|
|
## state - optionally while running other tasks.
|
|
#readChunks : Path, U64, state, (state, List U8 -> Task state []err) -> Task state (FileReadErr err)
|
|
|
|
## Like #readChunks except after each chunk you can either `Continue`,
|
|
## specifying how many bytes you'd like to read next, or `Stop` early.
|
|
#readChunksOrStop : Path, U64, state, (state, List U8 -> [ Continue U64 (Task state []err), Stop (Task state []err) ]) -> Task state (FileReadErr err)
|