Flesh out basic file I/O examples

This commit is contained in:
Richard Feldman 2022-09-11 16:55:36 -04:00
parent 224475a87d
commit 079311d080
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
5 changed files with 2765 additions and 40 deletions

View file

@ -1,5 +1,5 @@
interface File interface File
exposes [ReadErr, WriteErr, writeUtf8, writeBytes, readUtf8, readBytes] exposes [ReadErr, WriteErr, write, writeUtf8, writeBytes, readUtf8, readBytes]
imports [Effect, Task.{ Task }, InternalTask, InternalFile, Path.{ Path }, InternalPath] imports [Effect, Task.{ Task }, InternalTask, InternalFile, Path.{ Path }, InternalPath]
ReadErr : InternalFile.ReadErr ReadErr : InternalFile.ReadErr
@ -7,7 +7,7 @@ ReadErr : InternalFile.ReadErr
WriteErr : InternalFile.WriteErr WriteErr : InternalFile.WriteErr
## For example, suppose you have a [JSON](https://en.wikipedia.org/wiki/JSON) ## For example, suppose you have a [JSON](https://en.wikipedia.org/wiki/JSON)
## [EncodingFormat] named `Json.toCompactUtf8`. Then you can use that format ## `EncodingFormat` named `Json.toCompactUtf8`. Then you can use that format
## to write some encodable data to a file as JSON, like so: ## to write some encodable data to a file as JSON, like so:
## ##
## File.write ## File.write
@ -24,12 +24,12 @@ WriteErr : InternalFile.WriteErr
## This opens the file first and closes it after writing to it. ## This opens the file first and closes it after writing to it.
## ##
## To write unformatted bytes to a file, you can use [File.writeBytes] instead. ## To write unformatted bytes to a file, you can use [File.writeBytes] instead.
# write : Path, val, fmt -> Task {} (WriteErr *) [Write [File]*]* write : Path, val, fmt -> Task {} [FileWriteErr Path WriteErr]* [Write [File]*]*
# | val has Encode.Encoding, fmt has Encode.EncoderFormatting | val has Encode.Encoding, fmt has Encode.EncoderFormatting
# write = \path, val, fmt -> write = \path, val, fmt ->
# Encode.toBytes val fmt bytes = Encode.toBytes val fmt
# # TODO handle encoding errors here, once they exist # TODO handle encoding errors here, once they exist
# |> writeBytes writeBytes path bytes
## Write bytes to a file. ## Write bytes to a file.
## ##
@ -39,12 +39,12 @@ WriteErr : InternalFile.WriteErr
## This opens the file first and closes it after writing to it. ## This opens the file first and closes it after writing to it.
## ##
## To format data before writing it to a file, you can use [File.write] instead. ## To format data before writing it to a file, you can use [File.write] instead.
writeBytes : Path, List U8 -> Task {} [FileWriteErr WriteErr]* [Write [File]*]* writeBytes : Path, List U8 -> Task {} [FileWriteErr Path WriteErr]* [Write [File]*]*
writeBytes = \path, bytes -> writeBytes = \path, bytes ->
InternalPath.toBytes path InternalPath.toBytes path
|> Effect.fileWriteBytes bytes |> Effect.fileWriteBytes bytes
|> InternalTask.fromEffect |> InternalTask.fromEffect
|> Task.mapFail FileWriteErr |> Task.mapFail \err -> FileWriteErr path err
## Write a [Str] to a file, encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8). ## Write a [Str] to a file, encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8).
## ##
@ -54,12 +54,12 @@ writeBytes = \path, bytes ->
## This opens the file first and closes it after writing to it. ## This opens the file first and closes it after writing to it.
## ##
## To write unformatted bytes to a file, you can use [File.writeBytes] instead. ## To write unformatted bytes to a file, you can use [File.writeBytes] instead.
writeUtf8 : Path, Str -> Task {} [FileWriteErr WriteErr]* [Write [File]*]* writeUtf8 : Path, Str -> Task {} [FileWriteErr Path WriteErr]* [Write [File]*]*
writeUtf8 = \path, str -> writeUtf8 = \path, str ->
InternalPath.toBytes path InternalPath.toBytes path
|> Effect.fileWriteUtf8 str |> Effect.fileWriteUtf8 str
|> InternalTask.fromEffect |> InternalTask.fromEffect
|> Task.mapFail FileWriteErr |> Task.mapFail \err -> FileWriteErr path err
## Read all the bytes in a file. ## Read all the bytes in a file.
## ##
@ -68,13 +68,13 @@ writeUtf8 = \path, str ->
## ##
## This opens the file first and closes it after reading its contents. ## This opens the file first and closes it after reading its contents.
## ##
## To read and decode data from a file, you can use [File.read] instead. ## To read and decode data from a file, you can use `File.read` instead.
readBytes : Path -> Task (List U8) [FileReadErr ReadErr]* [Read [File]*]* readBytes : Path -> Task (List U8) [FileReadErr Path ReadErr]* [Read [File]*]*
readBytes = \path -> readBytes = \path ->
InternalPath.toBytes path InternalPath.toBytes path
|> Effect.fileReadBytes |> Effect.fileReadBytes
|> InternalTask.fromEffect |> InternalTask.fromEffect
|> Task.mapFail FileReadErr |> Task.mapFail \err -> FileReadErr path err
## Read a [Str] from a file containing [UTF-8](https://en.wikipedia.org/wiki/UTF-8)-encoded text. ## Read a [Str] from a file containing [UTF-8](https://en.wikipedia.org/wiki/UTF-8)-encoded text.
## ##
@ -89,32 +89,33 @@ readUtf8 :
Path Path
-> Task -> Task
Str Str
[FileReadErr ReadErr, FileReadUtf8Err _]* [FileReadErr Path ReadErr, FileReadUtf8Err Path _]*
[Read [File]*]* [Read [File]*]*
readUtf8 = \path -> readUtf8 = \path ->
effect = Effect.map (Effect.fileReadBytes (InternalPath.toBytes path)) \result -> effect = Effect.map (Effect.fileReadBytes (InternalPath.toBytes path)) \result ->
when result is when result is
Ok bytes -> Ok bytes ->
Str.fromUtf8 bytes Str.fromUtf8 bytes
|> Result.mapErr FileReadUtf8Err |> Result.mapErr \err -> FileReadUtf8Err path err
Err readErr -> Err (FileReadErr readErr) Err readErr -> Err (FileReadErr path readErr)
InternalTask.fromEffect effect InternalTask.fromEffect effect
# read : # read :
# Path # Path,
# fmt
# -> Task # -> Task
# Str # Str
# [FileReadErr ReadErr, FileReadDecodeErr DecodeErr]* # [FileReadErr Path ReadErr, FileReadDecodeErr Path [Leftover (List U8)]Decode.DecodeError ]*
# [Read [File]*]* # [Read [File]*]*
# | val has Decode.Decoding, fmt has Decode.DecoderFormatting # | val has Decode.Decoding, fmt has Decode.DecoderFormatting
# read = \path -> # read = \path, fmt ->
# effect = Effect.after (Effect.fileReadBytes path) \result -> # effect = Effect.map (Effect.fileReadBytes (InternalPath.toBytes path)) \result ->
# when result is # when result is
# Ok bytes -> # Ok bytes ->
# when Decode.fromBytes bytes fmt is # when Decode.fromBytes bytes fmt is
# Ok val -> InternalTask.succeed val # Ok val -> Ok val
# Err decodingErr -> Err (FileReadDecodeErr decodingErr) # Err decodingErr -> Err (FileReadDecodeErr decodingErr)
# Err readErr -> Err (FileReadErr readErr) # Err readErr -> Err (FileReadErr readErr)

View file

@ -2,6 +2,71 @@ interface InternalFile
exposes [ReadErr, WriteErr] exposes [ReadErr, WriteErr]
imports [] imports []
ReadErr : [NotFound, Other] ReadErr : [
NotFound,
Interrupted,
InvalidFilename,
PermissionDenied,
TooManySymlinks, # aka FilesystemLoop
TooManyHardlinks,
TimedOut,
StaleNetworkFileHandle,
OutOfMemory,
Unsupported,
Unrecognized I32 Str,
]
WriteErr : [PermissionDenied, Other] WriteErr : [
NotFound,
Interrupted,
InvalidFilename,
PermissionDenied,
TooManySymlinks, # aka FilesystemLoop
TooManyHardlinks,
TimedOut,
StaleNetworkFileHandle,
ReadOnlyFilesystem,
AlreadyExists, # can this happen here?
WasADirectory,
WriteZero, # TODO come up with a better name for this, or roll it into another error tag
StorageFull,
FilesystemQuotaExceeded, # can this be combined with StorageFull?
FileTooLarge,
ResourceBusy,
ExecutableFileBusy,
OutOfMemory,
Unsupported,
Unrecognized I32 Str,
]
# DirReadErr : [
# NotFound,
# Interrupted,
# InvalidFilename,
# PermissionDenied,
# TooManySymlinks, # aka FilesystemLoop
# TooManyHardlinks,
# TimedOut,
# StaleNetworkFileHandle,
# NotADirectory,
# OutOfMemory,
# Unsupported,
# Unrecognized I32 Str,
# ]
# RmDirError : [
# NotFound,
# Interrupted,
# InvalidFilename,
# PermissionDenied,
# TooManySymlinks, # aka FilesystemLoop
# TooManyHardlinks,
# TimedOut,
# StaleNetworkFileHandle,
# NotADirectory,
# ReadOnlyFilesystem,
# DirectoryNotEmpty,
# OutOfMemory,
# Unsupported,
# Unrecognized I32 Str,
# ]

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
mod file_glue;
mod glue; mod glue;
use core::alloc::Layout; use core::alloc::Layout;
@ -14,6 +15,9 @@ use std::os::raw::c_char;
use std::path::Path; use std::path::Path;
use std::time::Duration; use std::time::Duration;
use file_glue::ReadErr;
use file_glue::WriteErr;
extern "C" { extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"] #[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(output: *mut u8); fn roc_main(output: *mut u8);
@ -136,24 +140,35 @@ pub extern "C" fn roc_fx_stderrLine(line: &RocStr) {
eprintln!("{}", string); eprintln!("{}", string);
} }
// #[no_mangle]
// pub extern "C" fn roc_fx_fileWriteUtf8(
// roc_path: &RocList<u8>,
// roc_string: &RocStr,
// // ) -> RocResult<(), WriteErr> {
// ) -> (u8, u8) {
// let _ = write_slice(roc_path, roc_string.as_str().as_bytes());
// (255, 255)
// }
type Fail = Foo;
#[repr(C)] #[repr(C)]
pub enum ReadErr { pub struct Foo {
NotFound, data: u8,
Other, tag: u8,
}
#[repr(u8)]
pub enum WriteErr {
Other,
PermissionDenied,
} }
// #[no_mangle]
// pub extern "C" fn roc_fx_fileWriteUtf8(roc_path: &RocList<u8>, roc_string: &RocStr) -> Fail {
// write_slice2(roc_path, roc_string.as_str().as_bytes())
// }
#[no_mangle] #[no_mangle]
pub extern "C" fn roc_fx_fileWriteUtf8( pub extern "C" fn roc_fx_fileWriteUtf8(
roc_path: &RocList<u8>, roc_path: &RocList<u8>,
roc_string: &RocStr, roc_str: &RocStr,
) -> RocResult<(), WriteErr> { ) -> RocResult<(), WriteErr> {
write_slice(roc_path, roc_string.as_str().as_bytes()) write_slice(roc_path, roc_str.as_str().as_bytes())
} }
#[no_mangle] #[no_mangle]

View file

@ -11,6 +11,7 @@ main =
Task.attempt task \result -> Task.attempt task \result ->
when result is when result is
Ok {} -> Stdout.line "Successfully wrote a string to out.txt" Err (FileWriteErr _ PermissionDenied) -> Stderr.line "Err: PermissionDenied"
Err (FileWriteErr PermissionDenied) -> Stderr.line "Err: PermissionDenied" Err (FileWriteErr _ Unsupported) -> Stderr.line "Err: Unsupported"
Err (FileWriteErr Other) -> Stderr.line "Err: Other" Err (FileWriteErr _ (Unrecognized _ other)) -> Stderr.line "Err: \(other)"
_ -> Stdout.line "Successfully wrote a string to out.txt"