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 #[arg(long)]
17 test: bool,
18
19 #[arg(long, conflicts_with = "test")]
21 build: bool,
22
23 #[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!(
33 help = r#"Lux does not support nested shells.
34exit the current shell if you would like to enter a new one."#,
35 "already in a Lux shell"
36 ));
37 }
38
39 let tree = current_workspace_or_user_tree(&config)?;
40
41 let path = if data.build {
42 let build_tree_path = tree.build_tree(&config)?;
43 Paths::new(&build_tree_path)?
44 } else {
45 let mut path = Paths::new(&tree)?;
46 if data.test {
47 let test_tree_path = tree.test_tree(&config)?;
48 let test_path = Paths::new(&test_tree_path)?;
49 path.prepend(&test_path);
50 }
51 path
52 };
53
54 let shell: PathBuf = match env::var("SHELL") {
55 Ok(val) => PathBuf::from(val),
56 Err(_) => {
57 #[cfg(any(target_os = "linux", target_os = "android"))]
58 let fallback = which("bash")
59 .into_diagnostic()
60 .map_err(|_| miette!("cannot find `bash` on your system!"))?;
61
62 #[cfg(target_os = "windows")]
63 let fallback = which("cmd.exe")
64 .into_diagnostic()
65 .map_err(|_| miette!("cannot find `cmd.exe` on your system!"))?;
66
67 #[cfg(target_os = "macos")]
68 let fallback = which("zsh")
69 .into_diagnostic()
70 .map_err(|_| miette!("cannot find `zsh` on your system!"))?;
71
72 fallback
73 }
74 };
75
76 let lua_path = path.package_path_prepended();
77 let lua_cpath = path.package_cpath_prepended();
78
79 let lua_init = if data.no_loader {
80 None
81 } else if tree.version().lux_lib_dir().is_none() {
82 tracing::warn!(
83 r#"lux-lua library not found.
84Cannot use the `lux.loader`.
85To suppress this warning, set the `--no-loader` option."#
86 );
87 None
88 } else {
89 Some(path.init())
90 };
91
92 let lua_version = tree.version();
93
94 let mut bin_path = path.path_prepended();
95
96 let lua = LuaInstallation::new(lua_version, &config).await?;
97 if let Some(lua_bin_path) = lua.bin().as_ref().and_then(|lua_bin| lua_bin.parent()) {
98 bin_path.add_path(lua_bin_path.to_path_buf());
99 }
100
101 let _ = Command::new(&shell)
102 .env("PATH", bin_path.joined())
103 .env("LUA_PATH", lua_path.joined())
104 .env("LUA_CPATH", lua_cpath.joined())
105 .env("LUA_INIT", lua_init.unwrap_or_default())
106 .env("LUX_SHELL", "1")
107 .spawn()
108 .into_diagnostic()?
109 .wait()
110 .await
111 .into_diagnostic()?;
112
113 Ok(())
114}