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
exposes [ReadErr, WriteErr, writeUtf8, writeBytes, readUtf8, readBytes]
exposes [ReadErr, WriteErr, write, writeUtf8, writeBytes, readUtf8, readBytes]
imports [Effect, Task.{ Task }, InternalTask, InternalFile, Path.{ Path }, InternalPath]
ReadErr : InternalFile.ReadErr
@ -7,7 +7,7 @@ ReadErr : InternalFile.ReadErr
WriteErr : InternalFile.WriteErr
## 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:
##
## File.write
@ -24,12 +24,12 @@ WriteErr : InternalFile.WriteErr
## 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.
# write : Path, val, fmt -> Task {} (WriteErr *) [Write [File]*]*
# | val has Encode.Encoding, fmt has Encode.EncoderFormatting
# write = \path, val, fmt ->
# Encode.toBytes val fmt
# # TODO handle encoding errors here, once they exist
# |> writeBytes
write : Path, val, fmt -> Task {} [FileWriteErr Path WriteErr]* [Write [File]*]*
| val has Encode.Encoding, fmt has Encode.EncoderFormatting
write = \path, val, fmt ->
bytes = Encode.toBytes val fmt
# TODO handle encoding errors here, once they exist
writeBytes path bytes
## Write bytes to a file.
##
@ -39,12 +39,12 @@ WriteErr : InternalFile.WriteErr
## 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.
writeBytes : Path, List U8 -> Task {} [FileWriteErr WriteErr]* [Write [File]*]*
writeBytes : Path, List U8 -> Task {} [FileWriteErr Path WriteErr]* [Write [File]*]*
writeBytes = \path, bytes ->
InternalPath.toBytes path
|> Effect.fileWriteBytes bytes
|> 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).
##
@ -54,12 +54,12 @@ writeBytes = \path, bytes ->
## 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.
writeUtf8 : Path, Str -> Task {} [FileWriteErr WriteErr]* [Write [File]*]*
writeUtf8 : Path, Str -> Task {} [FileWriteErr Path WriteErr]* [Write [File]*]*
writeUtf8 = \path, str ->
InternalPath.toBytes path
|> Effect.fileWriteUtf8 str
|> InternalTask.fromEffect
|> Task.mapFail FileWriteErr
|> Task.mapFail \err -> FileWriteErr path err
## 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.
##
## To read and decode data from a file, you can use [File.read] instead.
readBytes : Path -> Task (List U8) [FileReadErr ReadErr]* [Read [File]*]*
## To read and decode data from a file, you can use `File.read` instead.
readBytes : Path -> Task (List U8) [FileReadErr Path ReadErr]* [Read [File]*]*
readBytes = \path ->
InternalPath.toBytes path
|> Effect.fileReadBytes
|> 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.
##
@ -89,32 +89,33 @@ readUtf8 :
Path
-> Task
Str
[FileReadErr ReadErr, FileReadUtf8Err _]*
[FileReadErr Path ReadErr, FileReadUtf8Err Path _]*
[Read [File]*]*
readUtf8 = \path ->
effect = Effect.map (Effect.fileReadBytes (InternalPath.toBytes path)) \result ->
when result is
Ok 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
# read :
# Path
# Path,
# fmt
# -> Task
# Str
# [FileReadErr ReadErr, FileReadDecodeErr DecodeErr]*
# [FileReadErr Path ReadErr, FileReadDecodeErr Path [Leftover (List U8)]Decode.DecodeError ]*
# [Read [File]*]*
# | val has Decode.Decoding, fmt has Decode.DecoderFormatting
# read = \path ->
# effect = Effect.after (Effect.fileReadBytes path) \result ->
# read = \path, fmt ->
# effect = Effect.map (Effect.fileReadBytes (InternalPath.toBytes path)) \result ->
# when result is
# Ok bytes ->
# when Decode.fromBytes bytes fmt is
# Ok val -> InternalTask.succeed val
# Ok val -> Ok val
# Err decodingErr -> Err (FileReadDecodeErr decodingErr)
# Err readErr -> Err (FileReadErr readErr)

View file

@ -2,6 +2,71 @@ interface InternalFile
exposes [ReadErr, WriteErr]
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)]
mod file_glue;
mod glue;
use core::alloc::Layout;
@ -14,6 +15,9 @@ use std::os::raw::c_char;
use std::path::Path;
use std::time::Duration;
use file_glue::ReadErr;
use file_glue::WriteErr;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(output: *mut u8);
@ -136,24 +140,35 @@ pub extern "C" fn roc_fx_stderrLine(line: &RocStr) {
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)]
pub enum ReadErr {
NotFound,
Other,
}
#[repr(u8)]
pub enum WriteErr {
Other,
PermissionDenied,
pub struct Foo {
data: u8,
tag: u8,
}
// #[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]
pub extern "C" fn roc_fx_fileWriteUtf8(
roc_path: &RocList<u8>,
roc_string: &RocStr,
roc_str: &RocStr,
) -> RocResult<(), WriteErr> {
write_slice(roc_path, roc_string.as_str().as_bytes())
write_slice(roc_path, roc_str.as_str().as_bytes())
}
#[no_mangle]

View file

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