lux_cli/
run_lua.rs

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    #[arg(long)]
20    test: bool,
21
22    #[arg(long)]
23    build: bool,
24
25    /// Arguments to pass to Lua. See `lua -h`.
26    args: Option<Vec<String>>,
27
28    /// Path to the Lua interpreter to use
29    #[arg(long)]
30    lua: Option<String>,
31
32    /// Print help
33    #[arg(long)]
34    help: bool,
35
36    #[clap(flatten)]
37    build_args: Build,
38}
39
40pub async fn run_lua(run_lua: RunLua, config: Config) -> Result<()> {
41    let project = Project::current()?;
42    let (lua_version, root, tree) = match &project {
43        Some(project) => (
44            project.toml().lua_version_matches(&config)?,
45            project.root().to_path_buf(),
46            project.tree(&config)?,
47        ),
48        None => {
49            let version = LuaVersion::from(&config)?.clone();
50            (
51                version.clone(),
52                std::env::current_dir()?,
53                config.user_tree(version)?,
54            )
55        }
56    };
57
58    let lua_cmd = run_lua
59        .lua
60        .map(LuaBinary::Custom)
61        .unwrap_or(LuaBinary::new(lua_version, &config));
62
63    if run_lua.help {
64        return print_lua_help(&lua_cmd).await;
65    }
66
67    if project.is_some() {
68        build::build(run_lua.build_args, config.clone()).await?;
69    }
70
71    let args = &run_lua.args.unwrap_or_default();
72
73    operations::RunLua::new()
74        .root(&root)
75        .tree(&tree)
76        .config(&config)
77        .lua_cmd(lua_cmd)
78        .args(args)
79        .prepend_test_paths(run_lua.test)
80        .prepend_build_paths(run_lua.build)
81        .run_lua()
82        .await?;
83
84    Ok(())
85}
86
87async fn print_lua_help(lua_cmd: &LuaBinary) -> Result<()> {
88    let output = match Command::new(lua_cmd.to_string())
89        // HACK: This fails with exit 1, because lua doesn't actually have a help flag (╯°□°)╯︵ ┻━┻
90        .arg("-h")
91        .output()
92        .await
93    {
94        Ok(output) => Ok(output),
95        Err(err) => Err(eyre!("Failed to run {}: {}", lua_cmd, err)),
96    }?;
97    let lua_help = String::from_utf8_lossy(&output.stderr)
98        .lines()
99        .skip(2)
100        .map(|line| format!("  {}", line))
101        .collect_vec()
102        .join("\n");
103    print!(
104        "
105Usage: lx lua -- [LUA_OPTIONS] [SCRIPT [ARGS]]...
106
107Arguments:
108  [LUA_OPTIONS]...
109{}
110
111Options:
112  --lua       Path to the Lua interpreter to use
113  --no-lock   When building a project, ignore the project's lockfile and don't create one
114  --test      Prepend test dependencies to the LUA_PATH and LUA_CPATH
115  --build     Prepend build dependencies to the LUA_PATH and LUA_CPATH
116  -h, --help  Print help
117",
118        lua_help,
119    );
120    Ok(())
121}
122
123#[cfg(test)]
124mod test {
125    use std::path::PathBuf;
126
127    use lux_lib::config::ConfigBuilder;
128    use serial_test::serial;
129
130    use super::*;
131
132    #[serial]
133    #[tokio::test]
134    async fn test_run_lua() {
135        let args = RunLua {
136            args: Some(vec!["-v".into()]),
137            ..RunLua::default()
138        };
139        let temp: PathBuf = assert_fs::TempDir::new().unwrap().path().into();
140        let cwd = &std::env::current_dir().unwrap();
141        tokio::fs::create_dir_all(&temp).await.unwrap();
142        std::env::set_current_dir(&temp).unwrap();
143        let config = ConfigBuilder::new()
144            .unwrap()
145            .user_tree(Some(temp.clone()))
146            .build()
147            .unwrap();
148        run_lua(args, config).await.unwrap();
149        std::env::set_current_dir(cwd).unwrap();
150    }
151}