1use tokio::process::Command;
2
3use clap::Args;
4use eyre::{eyre, Result};
5use itertools::Itertools;
6use lux_lib::{
7 config::{Config, LuaVersion},
8 lua_installation::LuaBinary,
9 operations,
10 project::Project,
11 rockspec::LuaVersionCompatibility,
12};
13
14use crate::build::{self, Build};
15
16#[derive(Args, Default)]
17#[clap(disable_help_flag = true)]
18pub struct RunLua {
19 args: Option<Vec<String>>,
21
22 #[arg(long)]
24 lua: Option<String>,
25
26 #[arg(long)]
28 help: bool,
29
30 #[clap(flatten)]
31 build: Build,
32}
33
34pub async fn run_lua(run_lua: RunLua, config: Config) -> Result<()> {
35 let project = Project::current()?;
36 let (lua_version, root, tree) = match &project {
37 Some(project) => (
38 project.toml().lua_version_matches(&config)?,
39 project.root().to_path_buf(),
40 project.tree(&config)?,
41 ),
42 None => {
43 let version = LuaVersion::from(&config)?.clone();
44 (
45 version.clone(),
46 std::env::current_dir()?,
47 config.user_tree(version)?,
48 )
49 }
50 };
51
52 let lua_cmd = run_lua
53 .lua
54 .map(LuaBinary::Custom)
55 .unwrap_or(LuaBinary::new(lua_version, &config));
56
57 if run_lua.help {
58 return print_lua_help(&lua_cmd).await;
59 }
60
61 if project.is_some() {
62 build::build(run_lua.build, config.clone()).await?;
63 }
64
65 operations::run_lua(&root, &tree, lua_cmd, &run_lua.args.unwrap_or_default()).await?;
66
67 Ok(())
68}
69
70async fn print_lua_help(lua_cmd: &LuaBinary) -> Result<()> {
71 let output = match Command::new(lua_cmd.to_string())
72 .arg("-h")
74 .output()
75 .await
76 {
77 Ok(output) => Ok(output),
78 Err(err) => Err(eyre!("Failed to run {}: {}", lua_cmd, err)),
79 }?;
80 let lua_help = String::from_utf8_lossy(&output.stderr)
81 .lines()
82 .skip(2)
83 .map(|line| format!(" {}", line))
84 .collect_vec()
85 .join("\n");
86 print!(
87 "
88Usage: lx lua -- [LUA_OPTIONS] [SCRIPT [ARGS]]...
89
90Arguments:
91 [LUA_OPTIONS]...
92{}
93
94Options:
95 --lua Path to the Lua interpreter to use
96 --no-lock When building a project, ignore the project's lockfile and don't create one
97 -h, --help Print help
98",
99 lua_help,
100 );
101 Ok(())
102}
103
104#[cfg(test)]
105mod test {
106 use std::path::PathBuf;
107
108 use lux_lib::config::ConfigBuilder;
109 use serial_test::serial;
110
111 use super::*;
112
113 #[serial]
114 #[tokio::test]
115 async fn test_run_lua() {
116 let args = RunLua {
117 args: Some(vec!["-v".into()]),
118 ..RunLua::default()
119 };
120 let temp: PathBuf = assert_fs::TempDir::new().unwrap().path().into();
121 let cwd = &std::env::current_dir().unwrap();
122 tokio::fs::create_dir_all(&temp).await.unwrap();
123 std::env::set_current_dir(&temp).unwrap();
124 let config = ConfigBuilder::new()
125 .unwrap()
126 .user_tree(Some(temp.clone()))
127 .build()
128 .unwrap();
129 run_lua(args, config).await.unwrap();
130 std::env::set_current_dir(cwd).unwrap();
131 }
132}