Skip to content

Commit

Permalink
Merge pull request #259 from roc-lang/stdin-read-to-end
Browse files Browse the repository at this point in the history
Add `Stdin.readtoEnd`
  • Loading branch information
lukewilliamboswell authored Nov 8, 2024
2 parents fbd1eac + a67f09a commit cbb6862
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 3 deletions.
104 changes: 104 additions & 0 deletions crates/roc_host/src/glue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use roc_std::roc_refcounted_noop_impl;
use roc_std::RocRefcounted;
use roc_std::RocStr;

#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(u8)]
pub enum InternalIOErrTag {
BrokenPipe = 0,
Interrupted = 1,
InvalidInput = 2,
Other = 3,
OutOfMemory = 4,
UnexpectedEof = 5,
Unsupported = 6,
WouldBlock = 7,
WriteZero = 8,
}

impl core::fmt::Debug for InternalIOErrTag {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::BrokenPipe => f.write_str("InternalIOErr::BrokenPipe"),
Self::Interrupted => f.write_str("InternalIOErr::Interrupted"),
Self::InvalidInput => f.write_str("InternalIOErr::InvalidInput"),
Self::Other => f.write_str("InternalIOErr::Other"),
Self::OutOfMemory => f.write_str("InternalIOErr::OutOfMemory"),
Self::UnexpectedEof => f.write_str("InternalIOErr::UnexpectedEof"),
Self::Unsupported => f.write_str("InternalIOErr::Unsupported"),
Self::WouldBlock => f.write_str("InternalIOErr::WouldBlock"),
Self::WriteZero => f.write_str("InternalIOErr::WriteZero"),
}
}
}

roc_refcounted_noop_impl!(InternalIOErrTag);

#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub struct InternalIOErr {
pub msg: roc_std::RocStr,
pub tag: InternalIOErrTag,
}

impl roc_std::RocRefcounted for InternalIOErr {
fn inc(&mut self) {
self.msg.inc();
}
fn dec(&mut self) {
self.msg.dec();
}
fn is_refcounted() -> bool {
true
}
}

impl From<std::io::Error> for InternalIOErr {
fn from(e: std::io::Error) -> Self {
match e.kind() {
std::io::ErrorKind::BrokenPipe => InternalIOErr {
tag: InternalIOErrTag::BrokenPipe,
msg: RocStr::empty(),
},
std::io::ErrorKind::OutOfMemory => InternalIOErr {
tag: InternalIOErrTag::OutOfMemory,
msg: RocStr::empty(),
},
std::io::ErrorKind::WriteZero => InternalIOErr {
tag: InternalIOErrTag::WriteZero,
msg: RocStr::empty(),
},
std::io::ErrorKind::ConnectionAborted
| std::io::ErrorKind::ConnectionReset
| std::io::ErrorKind::NotConnected
| std::io::ErrorKind::UnexpectedEof => InternalIOErr {
tag: InternalIOErrTag::UnexpectedEof,
msg: RocStr::empty(),
},
std::io::ErrorKind::Interrupted => InternalIOErr {
tag: InternalIOErrTag::Interrupted,
msg: RocStr::empty(),
},
std::io::ErrorKind::InvalidData | std::io::ErrorKind::InvalidInput => InternalIOErr {
tag: InternalIOErrTag::InvalidInput,
msg: RocStr::empty(),
},
std::io::ErrorKind::TimedOut => InternalIOErr {
tag: InternalIOErrTag::WouldBlock,
msg: RocStr::empty(),
},
std::io::ErrorKind::WouldBlock => InternalIOErr {
tag: InternalIOErrTag::WouldBlock,
msg: RocStr::empty(),
},
std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable => InternalIOErr {
tag: InternalIOErrTag::Unsupported,
msg: RocStr::empty(),
},
_ => InternalIOErr {
tag: InternalIOErrTag::Other,
msg: format!("{}", e).as_str().into(),
},
}
}
}
12 changes: 12 additions & 0 deletions crates/roc_host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{env, io};
use tokio::runtime::Runtime;

mod glue;

thread_local! {
static TOKIO_RUNTIME: Runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()
Expand Down Expand Up @@ -469,6 +471,16 @@ pub extern "C" fn roc_fx_stdinBytes() -> RocResult<RocList<u8>, ()> {
}
}

#[no_mangle]
pub extern "C" fn roc_fx_stdinReadToEnd() -> RocResult<RocList<u8>, glue::InternalIOErr> {
let stdin = std::io::stdin();
let mut buf = Vec::new();
match stdin.lock().read_to_end(&mut buf) {
Ok(bytes_read) => RocResult::ok(RocList::from(&buf[0..bytes_read])),
Err(err) => RocResult::err(err.into()),
}
}

/// See docs in `platform/Stdout.roc` for descriptions
fn handleStdoutErr(io_err: std::io::Error) -> RocStr {
match io_err.kind() {
Expand Down
2 changes: 1 addition & 1 deletion platform/Arg/Param.roc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ builderWithParameterParser : ParameterConfig, (List Str -> Result data ArgExtrac
builderWithParameterParser = \param, valueParser ->
argParser = \args ->
{ values, remainingArgs } = extractParamValues? { args, param }

data = valueParser? values

Ok { data, remainingArgs }
Expand Down
20 changes: 19 additions & 1 deletion platform/PlatformTasks.roc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
hosted PlatformTasks
exposes [
InternalIOErr,
args,
dirList,
dirCreate,
Expand All @@ -17,6 +18,7 @@ hosted PlatformTasks
stderrWrite,
stdinLine,
stdinBytes,
stdinReadToEnd,
ttyModeCanonical,
ttyModeRaw,
sendRequest,
Expand Down Expand Up @@ -49,12 +51,28 @@ hosted PlatformTasks
InternalPath,
]

InternalIOErr : {
tag : [
BrokenPipe,
WouldBlock,
WriteZero,
Unsupported,
Interrupted,
OutOfMemory,
UnexpectedEof,
InvalidInput,
Other,
],
msg : Str,
}

stdoutLine : Str -> Task {} Str
stdoutWrite : Str -> Task {} Str
stderrLine : Str -> Task {} Str
stderrWrite : Str -> Task {} Str
stdinLine : Task Str Str
stdinLine : Task Str Str
stdinBytes : Task (List U8) {}
stdinReadToEnd : Task (List U8) InternalIOErr
ttyModeCanonical : Task {} {}
ttyModeRaw : Task {} {}

Expand Down
23 changes: 22 additions & 1 deletion platform/Stdin.roc
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
module [line, bytes, Err]
module [
Err,
line,
bytes,
readToEnd,
]

import PlatformTasks

Expand Down Expand Up @@ -68,3 +73,19 @@ bytes = \{} ->
# will return an empty list if no bytes are available
PlatformTasks.stdinBytes
|> Task.mapErr \_ -> crash "unreachable"

## Read all bytes from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)) until EOF in this source.
readToEnd : {} -> Task (List U8) [StdinErr Err]
readToEnd = \{} ->
PlatformTasks.stdinReadToEnd
|> Task.mapErr \internalErr ->
when internalErr.tag is
BrokenPipe -> StdinErr BrokenPipe
WouldBlock -> StdinErr (Other "WouldBlock")
WriteZero -> StdinErr (Other "WriteZero")
Unsupported -> StdinErr Unsupported
Interrupted -> StdinErr Interrupted
OutOfMemory -> StdinErr OutOfMemory
UnexpectedEof -> StdinErr UnexpectedEof
InvalidInput -> StdinErr InvalidInput
Other -> StdinErr (Other internalErr.msg)
24 changes: 24 additions & 0 deletions platform/glue.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
platform ""
requires {} { main : _ }
exposes []
packages {}
imports []
provides [mainForHost]

InternalIOErr : {
tag : [
BrokenPipe,
WouldBlock,
WriteZero,
Unsupported,
Interrupted,
OutOfMemory,
UnexpectedEof,
InvalidInput,
Other,
],
msg : Str,
}

mainForHost : InternalIOErr
mainForHost = main

0 comments on commit cbb6862

Please sign in to comment.