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