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#[must_use]
42pub fn typedefs() -> String {
43 TYPEDEFS.to_string()
44}
45
46pub 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}