1#![allow(clippy::cargo_common_metadata)]
2
3use std::{
4 env::consts::{ARCH, OS},
5 path::MAIN_SEPARATOR,
6 process::Stdio,
7};
8
9use mlua::prelude::*;
10use mlua_luau_scheduler::Functions;
11
12use lune_utils::{
13 TableBuilder,
14 path::get_current_dir,
15 process::{ProcessArgs, ProcessEnv},
16};
17
18mod create;
19mod exec;
20mod options;
21
22use self::options::ProcessSpawnOptions;
23
24const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau"));
25
26#[must_use]
30pub fn typedefs() -> String {
31 TYPEDEFS.to_string()
32}
33
34#[allow(clippy::missing_panics_doc)]
42pub fn module(lua: Lua) -> LuaResult<LuaTable> {
43 let mut cwd_str = get_current_dir()
44 .to_str()
45 .expect("cwd should be valid UTF-8")
46 .to_string();
47 if !cwd_str.ends_with(MAIN_SEPARATOR) {
48 cwd_str.push(MAIN_SEPARATOR);
49 }
50
51 let os = lua.create_string(OS.to_lowercase())?;
53 let arch = lua.create_string(ARCH.to_lowercase())?;
54 let endianness = lua.create_string(if cfg!(target_endian = "big") {
55 "big"
56 } else {
57 "little"
58 })?;
59
60 let process_args = lua
63 .app_data_ref::<ProcessArgs>()
64 .ok_or_else(|| LuaError::runtime("Missing process args in Lua app data"))?
65 .into_plain_lua_table(lua.clone())?;
66 let process_env = lua
67 .app_data_ref::<ProcessEnv>()
68 .ok_or_else(|| LuaError::runtime("Missing process env in Lua app data"))?
69 .into_plain_lua_table(lua.clone())?;
70
71 process_args.set_readonly(true);
72
73 let fns = Functions::new(lua.clone())?;
75 let process_exit = fns.exit;
76
77 TableBuilder::new(lua)?
79 .with_value("os", os)?
80 .with_value("arch", arch)?
81 .with_value("endianness", endianness)?
82 .with_value("args", process_args)?
83 .with_value("cwd", cwd_str)?
84 .with_value("env", process_env)?
85 .with_value("exit", process_exit)?
86 .with_async_function("exec", process_exec)?
87 .with_function("create", process_create)?
88 .build_readonly()
89}
90
91async fn process_exec(
92 lua: Lua,
93 (program, args, mut options): (String, ProcessArgs, ProcessSpawnOptions),
94) -> LuaResult<LuaTable> {
95 let stdin = options.stdio.stdin.take();
96 let stdout = options.stdio.stdout;
97 let stderr = options.stdio.stderr;
98
99 let stdin_stdio = if stdin.is_some() {
100 Stdio::piped()
101 } else {
102 Stdio::null()
103 };
104
105 let child = options
106 .into_command(program, args)
107 .stdin(stdin_stdio)
108 .stdout(stdout.as_stdio())
109 .stderr(stderr.as_stdio())
110 .spawn()?;
111
112 exec::exec(lua, child, stdin, stdout, stderr).await
113}
114
115fn process_create(
116 lua: &Lua,
117 (program, args, options): (String, ProcessArgs, ProcessSpawnOptions),
118) -> LuaResult<LuaValue> {
119 let child = options
120 .into_command(program, args)
121 .stdin(Stdio::piped())
122 .stdout(Stdio::piped())
123 .stderr(Stdio::piped())
124 .spawn()?;
125
126 create::Child::new(lua, child).into_lua(lua)
127}