1use std::{env, str::FromStr as _};
2
3use clap::Subcommand;
4use eyre::Result;
5use lux_lib::{
6 config::Config,
7 path::{BinPath, PackagePath, Paths},
8};
9use strum_macros::{Display, EnumString, VariantNames};
10
11use clap::{Args, ValueEnum};
12
13use crate::utils::project::current_project_or_user_tree;
14
15#[derive(Args)]
16pub struct Path {
17 #[command(subcommand)]
18 cmd: Option<PathCmd>,
19
20 #[clap(default_value_t = false)]
22 #[arg(long)]
23 prepend: bool,
24}
25
26#[derive(Subcommand, PartialEq, Eq, Debug, Clone)]
27#[clap(rename_all = "kebab_case")]
28enum PathCmd {
29 Full(FullArgs),
32 Lua,
35 C,
38 Bin,
41 Init,
44}
45
46impl Default for PathCmd {
47 fn default() -> Self {
48 Self::Full(FullArgs::default())
49 }
50}
51
52#[derive(Args, PartialEq, Eq, Debug, Clone, Default)]
53struct FullArgs {
54 #[clap(default_value_t = false)]
56 #[arg(long)]
57 no_bin: bool,
58
59 #[clap(default_value_t = false)]
63 #[arg(long)]
64 no_loader: bool,
65
66 #[clap(default_value_t = Shell::default())]
68 #[arg(long)]
69 shell: Shell,
70}
71
72#[derive(EnumString, VariantNames, Display, ValueEnum, PartialEq, Eq, Debug, Clone)]
73#[strum(serialize_all = "lowercase")]
74enum Shell {
75 Posix,
76 Fish,
77 Nu,
78}
79
80impl Default for Shell {
81 fn default() -> Self {
82 Self::Posix
83 }
84}
85
86pub async fn path(path_data: Path, config: Config) -> Result<()> {
87 let tree = current_project_or_user_tree(&config)?;
88 let paths = Paths::new(&tree)?;
89 let cmd = path_data.cmd.unwrap_or_default();
90 let prepend = path_data.prepend;
91 match cmd {
92 PathCmd::Full(args) => {
93 let mut result = String::new();
94 let no_loader = args.no_loader || {
95 if tree.version().lux_lib_dir().is_none() {
96 eprintln!(
97 "⚠️ WARNING: lux-lua library not found.
98Cannot use the `lux.loader`.
99To suppress this warning, set the `--no-loader` option.
100 "
101 );
102 true
103 } else {
104 false
105 }
106 };
107 let shell = args.shell;
108 let package_path = mk_package_path(&paths, prepend)?;
109 if !package_path.is_empty() {
110 result.push_str(format_export(&shell, "LUA_PATH", &package_path).as_str());
111 result.push('\n')
112 }
113 let package_cpath = mk_package_cpath(&paths, prepend)?;
114 if !package_cpath.is_empty() {
115 result.push_str(format_export(&shell, "LUA_CPATH", &package_cpath).as_str());
116 result.push('\n')
117 }
118 if !args.no_bin {
119 let path = mk_bin_path(&paths, prepend)?;
120 if !path.is_empty() {
121 result.push_str(format_export(&shell, "PATH", &path).as_str());
122 result.push('\n')
123 }
124 }
125 if !no_loader {
126 result.push_str(format_export(&shell, "LUA_INIT", &paths.init()).as_str());
127 result.push('\n')
128 }
129 println!("{}", &result);
130 }
131 PathCmd::Lua => println!("{}", &mk_package_path(&paths, prepend)?),
132 PathCmd::C => println!("{}", &mk_package_cpath(&paths, prepend)?),
133 PathCmd::Bin => println!("{}", &mk_bin_path(&paths, prepend)?),
134 PathCmd::Init => println!("{}", paths.init()),
135 }
136 Ok(())
137}
138
139fn mk_package_path(paths: &Paths, prepend: bool) -> Result<PackagePath> {
140 let mut result = if prepend {
141 PackagePath::from_str(env::var("LUA_PATH").unwrap_or_default().as_str()).unwrap_or_default()
142 } else {
143 PackagePath::default()
144 };
145 result.prepend(paths.package_path());
146 Ok(result)
147}
148
149fn mk_package_cpath(paths: &Paths, prepend: bool) -> Result<PackagePath> {
150 let mut result = if prepend {
151 PackagePath::from_str(env::var("LUA_CPATH").unwrap_or_default().as_str())
152 .unwrap_or_default()
153 } else {
154 PackagePath::default()
155 };
156 result.prepend(paths.package_cpath());
157 Ok(result)
158}
159
160fn mk_bin_path(paths: &Paths, prepend: bool) -> Result<BinPath> {
161 let mut result = if prepend {
162 BinPath::from_env()
163 } else {
164 BinPath::default()
165 };
166 result.prepend(paths.path());
167 Ok(result)
168}
169
170fn format_export<D>(shell: &Shell, var_name: &str, var: &D) -> String
171where
172 D: std::fmt::Display,
173{
174 match shell {
175 Shell::Posix => format!("export {}='{}';", var_name, var),
176 Shell::Fish => format!("set -x {} \"{}\";", var_name, var),
177 Shell::Nu => format!("$env.{} = \"{}\";", var_name, var),
178 }
179}