From 6b45f44d1fef6cb96ba7a3fca7ffdf702134f836 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Thu, 24 Apr 2025 18:53:17 +0200 Subject: [PATCH] Implement readLine in lune-std-stdio --- Cargo.lock | 1 + crates/lune-std-stdio/Cargo.toml | 1 + crates/lune-std-stdio/src/lib.rs | 36 ++++++++++++++++++++------------ types/stdio.luau | 28 ++++++++++++++++--------- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90fb508..3e1eb86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1776,6 +1776,7 @@ name = "lune-std-stdio" version = "0.1.2" dependencies = [ "async-io", + "async-lock", "blocking", "dialoguer", "futures-lite", diff --git a/crates/lune-std-stdio/Cargo.toml b/crates/lune-std-stdio/Cargo.toml index e1ce87a..facaac9 100644 --- a/crates/lune-std-stdio/Cargo.toml +++ b/crates/lune-std-stdio/Cargo.toml @@ -17,6 +17,7 @@ mlua = { version = "0.10.3", features = ["luau", "error-send"] } mlua-luau-scheduler = { version = "0.0.2", path = "../mlua-luau-scheduler" } async-io = "2.4" +async-lock = "3.4" blocking = "1.6" dialoguer = "0.11" futures-lite = "2.6" diff --git a/crates/lune-std-stdio/src/lib.rs b/crates/lune-std-stdio/src/lib.rs index 3137136..521973d 100644 --- a/crates/lune-std-stdio/src/lib.rs +++ b/crates/lune-std-stdio/src/lib.rs @@ -1,12 +1,16 @@ #![allow(clippy::cargo_common_metadata)] -use std::io::{stderr, stdin, stdout}; +use std::{ + io::{stderr, stdin, stdout, Stdin}, + sync::{Arc, LazyLock}, +}; use mlua::prelude::*; use mlua_luau_scheduler::LuaSpawnExt; +use async_lock::Mutex as AsyncMutex; use blocking::Unblock; -use futures_lite::prelude::*; +use futures_lite::{io::BufReader, prelude::*}; use lune_utils::{ fmt::{pretty_format_multi_value, ValueFormatConfig}, @@ -23,6 +27,12 @@ const FORMAT_CONFIG: ValueFormatConfig = ValueFormatConfig::new() .with_max_depth(4) .with_colors_enabled(false); +static STDIN: LazyLock>>>> = LazyLock::new(|| { + let stdin = Unblock::new(stdin()); + let reader = BufReader::new(stdin); + Arc::new(AsyncMutex::new(reader)) +}); + /** Creates the `stdio` standard library module. @@ -37,6 +47,7 @@ pub fn module(lua: Lua) -> LuaResult { .with_function("format", stdio_format)? .with_async_function("write", stdio_write)? .with_async_function("ewrite", stdio_ewrite)? + .with_async_function("readLine", stdio_read_line)? .with_async_function("readToEnd", stdio_read_to_end)? .with_async_function("prompt", stdio_prompt)? .build_readonly() @@ -68,19 +79,18 @@ async fn stdio_ewrite(_: Lua, s: LuaString) -> LuaResult<()> { Ok(()) } -/* - FUTURE: Figure out how to expose some kind of "readLine" function using a buffered reader. - - This is a bit tricky since we would want to be able to use **both** readLine and readToEnd - in the same script, doing something like readLine, readLine, readToEnd from lua, and - having that capture the first two lines and then read the rest of the input. -*/ +async fn stdio_read_line(lua: Lua, (): ()) -> LuaResult { + let mut string = String::new(); + let mut handle = STDIN.lock_arc().await; + handle.read_line(&mut string).await?; + lua.create_string(&string) +} async fn stdio_read_to_end(lua: Lua, (): ()) -> LuaResult { - let mut input = Vec::new(); - let mut stdin = Unblock::new(stdin()); - stdin.read_to_end(&mut input).await?; - lua.create_string(&input) + let mut buffer = Vec::new(); + let mut handle = STDIN.lock_arc().await; + handle.read_to_end(&mut buffer).await?; + lua.create_string(&buffer) } async fn stdio_prompt(lua: Lua, options: PromptOptions) -> LuaResult { diff --git a/types/stdio.luau b/types/stdio.luau index e6e88a4..06ebc27 100644 --- a/types/stdio.luau +++ b/types/stdio.luau @@ -1,13 +1,4 @@ -export type Color = - "reset" - | "black" - | "red" - | "green" - | "yellow" - | "blue" - | "purple" - | "cyan" - | "white" +export type Color = "reset" | "black" | "red" | "green" | "yellow" | "blue" | "purple" | "cyan" | "white" export type Style = "reset" | "bold" | "dim" type PromptFn = ( @@ -59,6 +50,9 @@ end stdio.write("All on the same line") stdio.ewrite("\nAnd some error text, too") + -- Reading a single line from stdin + local line = stdio.readLine() + -- Reading the entire input from stdin local input = stdio.readToEnd() ``` @@ -146,6 +140,20 @@ function stdio.write(s: string) end ]=] function stdio.ewrite(s: string) end +--[=[ + @within Stdio + @tag must_use + + Reads a single line from stdin. + + If stdin is closed, returns all input up until its closure. + + @return The input from stdin +]=] +function stdio.readLine(): string + return nil :: any +end + --[=[ @within Stdio @tag must_use