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