lune_std_stdio/
lib.rs

1#![allow(clippy::cargo_common_metadata)]
2
3use std::{
4    io::{Stdin, stderr, stdin, stdout},
5    sync::{Arc, LazyLock},
6};
7
8use mlua::prelude::*;
9use mlua_luau_scheduler::LuaSpawnExt;
10
11use async_lock::Mutex as AsyncMutex;
12use blocking::Unblock;
13use futures_lite::{io::BufReader, prelude::*};
14
15use lune_utils::{
16    TableBuilder,
17    fmt::{ValueFormatConfig, pretty_format_multi_value},
18};
19
20mod prompt;
21mod style_and_color;
22
23use self::prompt::{PromptOptions, PromptResult, prompt};
24use self::style_and_color::{ColorKind, StyleKind};
25
26const FORMAT_CONFIG: ValueFormatConfig = ValueFormatConfig::new()
27    .with_max_depth(4)
28    .with_colors_enabled(false);
29
30static STDIN: LazyLock<Arc<AsyncMutex<BufReader<Unblock<Stdin>>>>> = LazyLock::new(|| {
31    let stdin = Unblock::new(stdin());
32    let reader = BufReader::new(stdin);
33    Arc::new(AsyncMutex::new(reader))
34});
35
36const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau"));
37
38/**
39    Returns a string containing type definitions for the `stdio` standard library.
40*/
41#[must_use]
42pub fn typedefs() -> String {
43    TYPEDEFS.to_string()
44}
45
46/**
47    Creates the `stdio` standard library module.
48
49    # Errors
50
51    Errors when out of memory.
52*/
53pub fn module(lua: Lua) -> LuaResult<LuaTable> {
54    TableBuilder::new(lua)?
55        .with_function("color", stdio_color)?
56        .with_function("style", stdio_style)?
57        .with_function("format", stdio_format)?
58        .with_async_function("write", stdio_write)?
59        .with_async_function("ewrite", stdio_ewrite)?
60        .with_async_function("readLine", stdio_read_line)?
61        .with_async_function("readToEnd", stdio_read_to_end)?
62        .with_async_function("prompt", stdio_prompt)?
63        .build_readonly()
64}
65
66fn stdio_color(lua: &Lua, color: ColorKind) -> LuaResult<LuaValue> {
67    color.ansi_escape_sequence().into_lua(lua)
68}
69
70fn stdio_style(lua: &Lua, style: StyleKind) -> LuaResult<LuaValue> {
71    style.ansi_escape_sequence().into_lua(lua)
72}
73
74fn stdio_format(_: &Lua, args: LuaMultiValue) -> LuaResult<String> {
75    Ok(pretty_format_multi_value(&args, &FORMAT_CONFIG))
76}
77
78async fn stdio_write(_: Lua, s: LuaString) -> LuaResult<()> {
79    let mut stdout = Unblock::new(stdout());
80    stdout.write_all(&s.as_bytes()).await?;
81    stdout.flush().await?;
82    Ok(())
83}
84
85async fn stdio_ewrite(_: Lua, s: LuaString) -> LuaResult<()> {
86    let mut stderr = Unblock::new(stderr());
87    stderr.write_all(&s.as_bytes()).await?;
88    stderr.flush().await?;
89    Ok(())
90}
91
92async fn stdio_read_line(lua: Lua, (): ()) -> LuaResult<LuaString> {
93    let mut string = String::new();
94    let mut handle = STDIN.lock_arc().await;
95    handle.read_line(&mut string).await?;
96    lua.create_string(&string)
97}
98
99async fn stdio_read_to_end(lua: Lua, (): ()) -> LuaResult<LuaString> {
100    let mut buffer = Vec::new();
101    let mut handle = STDIN.lock_arc().await;
102    handle.read_to_end(&mut buffer).await?;
103    lua.create_string(&buffer)
104}
105
106async fn stdio_prompt(lua: Lua, options: PromptOptions) -> LuaResult<PromptResult> {
107    lua.spawn_blocking(move || prompt(options))
108        .await
109        .into_lua_err()
110}