mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Flesh out basic file I/O examples
This commit is contained in:
parent
224475a87d
commit
079311d080
5 changed files with 2765 additions and 40 deletions
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
# ]
|
2643
examples/interactive/cli-platform/src/file_glue.rs
Normal file
2643
examples/interactive/cli-platform/src/file_glue.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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]
|
||||||
|
|
|
@ -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"
|
Loading…
Add table
Add a link
Reference in a new issue