lune_std_stdio/
lib.rs

1#![allow(clippy::cargo_common_metadata)]
2
3use lune_utils::fmt::{pretty_format_multi_value, ValueFormatConfig};
4use mlua::prelude::*;
5use mlua_luau_scheduler::LuaSpawnExt;
6
7use tokio::io::{stderr, stdin, stdout, AsyncReadExt, AsyncWriteExt};
8
9use lune_utils::TableBuilder;
10
11mod prompt;
12mod style_and_color;
13
14use self::prompt::{prompt, PromptOptions, PromptResult};
15use self::style_and_color::{ColorKind, StyleKind};
16
17const FORMAT_CONFIG: ValueFormatConfig = ValueFormatConfig::new()
18    .with_max_depth(4)
19    .with_colors_enabled(false);
20
21/**
22    Creates the `stdio` standard library module.
23
24    # Errors
25
26    Errors when out of memory.
27*/
28pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
29    TableBuilder::new(lua)?
30        .with_function("color", stdio_color)?
31        .with_function("style", stdio_style)?
32        .with_function("format", stdio_format)?
33        .with_async_function("write", stdio_write)?
34        .with_async_function("ewrite", stdio_ewrite)?
35        .with_async_function("readToEnd", stdio_read_to_end)?
36        .with_async_function("prompt", stdio_prompt)?
37        .build_readonly()
38}
39
40fn stdio_color(lua: &Lua, color: ColorKind) -> LuaResult<LuaValue> {
41    color.ansi_escape_sequence().into_lua(lua)
42}
43
44fn stdio_style(lua: &Lua, style: StyleKind) -> LuaResult<LuaValue> {
45    style.ansi_escape_sequence().into_lua(lua)
46}
47
48fn stdio_format(_: &Lua, args: LuaMultiValue) -> LuaResult<String> {
49    Ok(pretty_format_multi_value(&args, &FORMAT_CONFIG))
50}
51
52async fn stdio_write(_: &Lua, s: LuaString<'_>) -> LuaResult<()> {
53    let mut stdout = stdout();
54    stdout.write_all(s.as_bytes()).await?;
55    stdout.flush().await?;
56    Ok(())
57}
58
59async fn stdio_ewrite(_: &Lua, s: LuaString<'_>) -> LuaResult<()> {
60    let mut stderr = stderr();
61    stderr.write_all(s.as_bytes()).await?;
62    stderr.flush().await?;
63    Ok(())
64}
65
66/*
67    FUTURE: Figure out how to expose some kind of "readLine" function using a buffered reader.
68
69    This is a bit tricky since we would want to be able to use **both** readLine and readToEnd
70    in the same script, doing something like readLine, readLine, readToEnd from lua, and
71    having that capture the first two lines and then read the rest of the input.
72*/
73
74async fn stdio_read_to_end(lua: &Lua, (): ()) -> LuaResult<LuaString> {
75    let mut input = Vec::new();
76    let mut stdin = stdin();
77    stdin.read_to_end(&mut input).await?;
78    lua.create_string(&input)
79}
80
81async fn stdio_prompt(lua: &Lua, options: PromptOptions) -> LuaResult<PromptResult> {
82    lua.spawn_blocking(move || prompt(options))
83        .await
84        .into_lua_err()
85}