Skip to main content

lux_cli/
shell.rs

1use clap::Args;
2use lux_lib::{config::Config, lua_installation::LuaInstallation, path::Paths, tree::InstallTree};
3
4use miette::{miette, IntoDiagnostic, Result};
5use which::which;
6
7use std::{env, path::PathBuf};
8use tokio::process::Command;
9
10use super::workspace::current_workspace_or_user_tree;
11
12#[derive(Args)]
13pub struct Shell {
14    /// Add test dependencies to the shell's paths,{n}
15    /// in addition to the regular dependencies.
16    #[arg(long)]
17    test: bool,
18
19    /// Add *only* build dependencies to the shell's paths.
20    #[arg(long, conflicts_with = "test")]
21    build: bool,
22
23    /// Disable the Lux loader.{n}
24    /// If a rock has conflicting transitive dependencies,{n}
25    /// disabling the Lux loader may result in the wrong modules being loaded.{n}
26    #[arg(long)]
27    no_loader: bool,
28}
29
30pub async fn shell(data: Shell, config: Config) -> Result<()> {
31    if env::var("LUX_SHELL").is_ok_and(|lx_shell_var| lx_shell_var == "1") {
32        return Err(miette!("Already in a Lux shell."));
33    }
34
35    let tree = current_workspace_or_user_tree(&config)?;
36
37    let path = if data.build {
38        let build_tree_path = tree.build_tree(&config)?;
39        Paths::new(&build_tree_path)?
40    } else {
41        let mut path = Paths::new(&tree)?;
42        if data.test {
43            let test_tree_path = tree.test_tree(&config)?;
44            let test_path = Paths::new(&test_tree_path)?;
45            path.prepend(&test_path);
46        }
47        path
48    };
49
50    let shell: PathBuf = match env::var("SHELL") {
51        Ok(val) => PathBuf::from(val),
52        Err(_) => {
53            #[cfg(any(target_os = "linux", target_os = "android"))]
54            let fallback = which("bash")
55                .into_diagnostic()
56                .map_err(|_| miette!("Cannot find `bash` on your system!"))?;
57
58            #[cfg(target_os = "windows")]
59            let fallback = which("cmd.exe")
60                .into_diagnostic()
61                .map_err(|_| miette!("Cannot find `cmd.exe` on your system!"))?;
62
63            #[cfg(target_os = "macos")]
64            let fallback = which("zsh")
65                .into_diagnostic()
66                .map_err(|_| miette!("Cannot find `zsh` on your system!"))?;
67
68            fallback
69        }
70    };
71
72    let lua_path = path.package_path_prepended();
73    let lua_cpath = path.package_cpath_prepended();
74
75    let lua_init = if data.no_loader {
76        None
77    } else if tree.version().lux_lib_dir().is_none() {
78        tracing::warn!(
79            r#"lux-lua library not found.
80Cannot use the `lux.loader`.
81To suppress this warning, set the `--no-loader` option."#
82        );
83        None
84    } else {
85        Some(path.init())
86    };
87
88    let lua_version = tree.version();
89
90    let mut bin_path = path.path_prepended();
91
92    let lua = LuaInstallation::new(lua_version, &config).await?;
93    if let Some(lua_bin_path) = lua.bin().as_ref().and_then(|lua_bin| lua_bin.parent()) {
94        bin_path.add_path(lua_bin_path.to_path_buf());
95    }
96
97    let _ = Command::new(&shell)
98        .env("PATH", bin_path.joined())
99        .env("LUA_PATH", lua_path.joined())
100        .env("LUA_CPATH", lua_cpath.joined())
101        .env("LUA_INIT", lua_init.unwrap_or_default())
102        .env("LUX_SHELL", "1")
103        .spawn()
104        .into_diagnostic()?
105        .wait()
106        .await
107        .into_diagnostic()?;
108
109    Ok(())
110}