lux_cli/lib.rs
1use crate::{completion::Completion, dist::Dist, format::Fmt, project::NewProject};
2use std::error::Error;
3use std::path::PathBuf;
4
5use add::Add;
6use build::Build;
7use check::Check;
8use clap::{Parser, Subcommand};
9use config::ConfigCmd;
10use debug::Debug;
11use doc::Doc;
12use download::Download;
13use exec::Exec;
14use generate_rockspec::GenerateRockspec;
15use info::Info;
16use install::Install;
17use install_rockspec::InstallRockspec;
18use lint::Lint;
19use list::ListCmd;
20use lux_lib::lua_version::LuaVersion;
21use outdated::Outdated;
22use pack::Pack;
23use path::Path;
24use pin::ChangePin;
25use remove::Remove;
26use run::Run;
27use run_lua::RunLua;
28use search::Search;
29use shell::Shell;
30use sync::SyncProject;
31use test::Test;
32use uninstall::Uninstall;
33use update::Update;
34use upload::Upload;
35use url::Url;
36use vendor::Vendor;
37use which::Which;
38
39pub mod add;
40pub mod args;
41pub mod build;
42pub mod check;
43pub mod completion;
44pub mod config;
45pub mod debug;
46pub mod dist;
47pub mod doc;
48pub mod download;
49pub mod exec;
50pub mod fetch;
51pub mod format;
52pub mod generate_rockspec;
53pub mod info;
54pub mod install;
55pub mod install_lua;
56pub mod install_rockspec;
57pub mod lint;
58pub mod list;
59pub mod outdated;
60pub mod pack;
61pub mod path;
62pub mod pin;
63pub mod progress;
64pub mod project;
65pub mod purge;
66pub mod remove;
67pub mod run;
68pub mod run_lua;
69pub mod search;
70pub mod shell;
71pub mod sync;
72pub mod test;
73pub mod uninstall;
74pub mod unpack;
75pub mod update;
76pub mod upload;
77pub mod utils;
78pub mod vendor;
79pub mod which;
80pub mod workspace;
81
82/// A luxurious package manager for Lua.
83#[derive(Parser)]
84#[command(author, version, about, long_about = None, arg_required_else_help = true)]
85pub struct Cli {
86 /// Enable the sub-repositories in luarocks servers for rockspecs of in-development versions.
87 #[arg(long)]
88 pub dev: bool,
89
90 /// Fetch rocks/rockspecs from this server (takes priority over config file).
91 #[arg(long, value_name = "server")]
92 pub server: Option<Url>,
93
94 /// Fetch rocks/rockspecs from these servers in addition to the main server{n}
95 /// (overrides any entries in the config file).
96 #[arg(long, value_name = "extra-server")]
97 pub extra_servers: Option<Vec<Url>>,
98
99 /// Specify the luarocks server namespace to use.
100 #[arg(long, value_name = "namespace")]
101 pub namespace: Option<String>,
102
103 /// Specify the directory in which to install Lua if not found.
104 #[arg(long, value_name = "prefix")]
105 pub lua_dir: Option<PathBuf>,
106
107 /// Which Lua installation to use.{n}
108 /// Valid versions are: '5.1', '5.2', '5.3', '5.4', 'jit' and 'jit52'.
109 #[arg(long, value_name = "ver")]
110 pub lua_version: Option<LuaVersion>,
111
112 /// Which tree to operate on.
113 #[arg(long, value_name = "tree")]
114 pub tree: Option<PathBuf>,
115
116 /// Specifies the cache directory, e.g. for luarocks manifests.
117 #[arg(long, value_name = "cache-dir")]
118 pub cache_dir: Option<PathBuf>,
119
120 /// Specifies the data directory,{n}
121 /// in which the default user install tree resides{n}
122 /// (e.g. ~/.local/share/lux).
123 #[arg(long, value_name = "data-dir")]
124 pub data_dir: Option<PathBuf>,
125
126 /// Specifies a directory with locally vendored sources and RockSpecs.{n}
127 /// When building or installing a package with this flag,{n}
128 /// Lux will fetch sources from the <vendor-dir> instead of from a remote server.
129 #[arg(long, value_name = "vendor-dir")]
130 pub vendor_dir: Option<PathBuf>,
131
132 /// Override config variables.{n}
133 /// Example: `lx -v "LUA=/path/to/lua" ...`
134 #[arg(long, value_name = "variable", visible_short_alias = 'v', value_parser = parse_key_val::<String, String>)]
135 pub variables: Option<Vec<(String, String)>>,
136
137 /// Display verbose output of commands executed.
138 #[arg(long)]
139 pub verbose: bool,
140
141 /// Don't print any progress bars or spinners.
142 #[arg(long)]
143 pub no_progress: bool,
144
145 /// Skip prompts, selecting the default option.
146 #[arg(long)]
147 pub no_prompt: bool,
148
149 /// Configure lux for installing Neovim packages.
150 #[arg(long)]
151 pub nvim: bool,
152
153 /// Timeout on network operations, in seconds.{n}
154 /// 0 means no timeout (wait forever). Default is 30.
155 #[arg(long, value_name = "seconds")]
156 pub timeout: Option<usize>,
157
158 /// Maximum buffer size for parallel jobs, such as downloading rockspecs and installing rocks.
159 /// 0 means no limit. Default is 0.
160 #[arg(long, visible_short_alias = 'j')]
161 pub max_jobs: Option<usize>,
162
163 /// Do not generate or update a `.luarc.json` file when building{n}
164 /// a project.
165 #[arg(long)]
166 pub no_luarc: bool,
167
168 /// Do not wrap Lua `bin` scripts.
169 #[arg(long)]
170 pub no_wrap_bin: bool,
171
172 /// The user agent to set when making web requests.
173 /// Default is "lux/<version>"
174 #[arg(long)]
175 pub user_agent: Option<String>,
176
177 #[command(subcommand)]
178 pub command: Commands,
179}
180
181#[derive(Subcommand)]
182pub enum Commands {
183 /// Add a dependency to the current project.
184 Add(Add),
185 /// Build/compile a project.
186 Build(Build),
187 /// [EXPERIMENTAL]{n}
188 /// Type check the current project based on EmmyLua/LuaCATS annotations.{n}
189 /// Respects `.emmyrc.json` and `.luarc.json` files in the project directory.
190 Check(Check),
191 /// Interact with the lux configuration.
192 #[command(subcommand, arg_required_else_help = true)]
193 Config(ConfigCmd),
194 /// Generate autocompletion scripts for the shell.{n}
195 /// Example: `lx completion zsh > ~/.zsh/completions/_lx`
196 Completion(Completion),
197 /// Internal commands for debugging Lux itself.
198 #[command(subcommand, arg_required_else_help = true)]
199 Debug(Debug),
200 /// Distribute a Lux project.
201 #[command(subcommand, arg_required_else_help = true)]
202 Dist(Dist),
203 /// Show documentation for an installed rock.
204 Doc(Doc),
205 /// Download a specific rock file from a luarocks server.
206 #[command(arg_required_else_help = true)]
207 Download(Download),
208 /// Formats the codebase with stylua.
209 Fmt(Fmt),
210 /// Generate a rockspec file from a project.
211 GenerateRockspec(GenerateRockspec),
212 /// Show metadata for any rock.
213 Info(Info),
214 /// Install a rock for use on the system.
215 #[command(arg_required_else_help = true)]
216 Install(Install),
217 /// Install a local rockspec for use on the system.
218 #[command(arg_required_else_help = true)]
219 InstallRockspec(InstallRockspec),
220 /// Manually install and manage Lua headers for various Lua versions.
221 InstallLua,
222 /// Lint the current project using `luacheck`.
223 Lint(Lint),
224 /// List currently installed rocks.
225 List(ListCmd),
226 /// Run lua, with the `LUA_PATH` and `LUA_CPATH` set to the specified lux tree.
227 Lua(RunLua),
228 /// Create a new Lua project.
229 New(NewProject),
230 /// List outdated rocks.
231 Outdated(Outdated),
232 /// Create a packed rock for distribution, packing sources or binaries.
233 Pack(Pack),
234 /// Return the currently configured package path.
235 Path(Path),
236 /// Pin an existing rock, preventing any updates to the package.
237 Pin(ChangePin),
238 /// Remove all installed rocks from a tree.
239 Purge,
240 /// Remove a rock from the current project's lux.toml dependencies.
241 Remove(Remove),
242 /// Run the current project with the provided arguments.
243 Run(Run),
244 /// Execute a command that has been installed with lux.
245 /// If the command is not found, a package named after the command
246 /// will be installed.
247 Exec(Exec),
248 /// Query the luarocks servers.
249 #[command(arg_required_else_help = true)]
250 Search(Search),
251 /// Run the test suite in the current project directory.{n}
252 /// Lux supports the following test backends, specified by the `[test]` table in the lux.toml:{n}
253 /// {n}
254 /// - busted:{n}
255 /// {n}
256 /// https://lunarmodules.github.io/busted/{n}
257 /// {n}
258 /// Example:{n}
259 /// {n}
260 /// ```toml{n}
261 /// [test]{n}
262 /// type = "busted"{n}
263 /// flags = [ ] # Optional CLI flags to pass to busted{n}
264 /// ```{n}
265 /// {n}
266 /// `lx test` will default to using `busted` if no test backend is specified and:{n}
267 /// * there is a `.busted` file in the project root{n}
268 /// * or `busted` is one of the `test_dependencies`).{n}
269 /// {n}
270 /// - busted-nlua:{n}:
271 /// {n}
272 /// [currently broken on Windows]{n}
273 /// A build backend for running busted tests with Neovim as the Lua interpreter.
274 /// Used for testing Neovim plugins.
275 /// {n}
276 /// Example:{n}
277 /// {n}
278 /// ```toml{n}
279 /// [test]{n}
280 /// type = "busted-nlua"{n}
281 /// flags = [ ] # Optional CLI flags to pass to busted{n}
282 /// ```{n}
283 /// {n}
284 /// `lx test` will default to using `busted-nlua` if no test backend is specified and:{n}
285 /// * there is a `.busted` file in the project root{n}
286 /// * or `busted` and `nlua` are `test_dependencies`.{n}
287 /// {n}
288 /// - command:{n}
289 /// {n}
290 /// Name/file name of a shell command that will run the test suite.{n}
291 /// Example:{n}
292 /// {n}
293 /// ```toml{n}
294 /// [test]{n}
295 /// type = "command"{n}
296 /// command = "make"{n}
297 /// flags = [ "test" ]{n}
298 /// ```{n}
299 /// {n}
300 /// - script:{n}
301 /// {n}
302 /// Relative path to a Lua script that will run the test suite.{n}
303 /// Example:{n}
304 /// {n}
305 /// ```toml{n}
306 /// [test]{n}
307 /// type = "script"{n}
308 /// script = "tests.lua" # Expects a tests.lua file in the project root{n}
309 /// flags = [ ] # Optional arguments passed to the test script{n}
310 /// ```{n}
311 Test(Test),
312 /// Uninstall a rock from the system.
313 Uninstall(Uninstall),
314 /// Unpins an existing rock, allowing updates to alter the package.
315 Unpin(ChangePin),
316 /// Updates all rocks in a project.
317 Update(Update),
318 /// Generate a Lua rockspec for a Lux project and upload it to the public luarocks repository.{n}
319 /// You can specify a source template for release and dev packages in the lux.toml.{n}
320 /// {n}
321 /// Example:{n}
322 /// {n}
323 /// ```toml{n}
324 /// [source]{n}
325 /// url = "https://host.com/owner/$(PACKAGE)/refs/tags/$(REF).zip"{n}
326 /// dev = "git+https://host.com/owner/$(PACKAGE).git"{n}
327 /// ```{n}
328 /// {n}
329 /// You can use the following variables in the source template:{n}
330 /// {n}
331 /// - $(PACKAGE): The package name.{n}
332 /// - $(VERSION): The package version.{n}
333 /// - $(REF): The git tag or revision (if in a git repository).{n}
334 /// - You may also specify environment variables with `$(<VAR_NAME>)`.{n}
335 /// {n}
336 /// If the `version` is not set in the lux.toml, lux will search the current
337 /// commit for SemVer tags and if found, will use it to generate the package version.
338 Upload(Upload),
339 /// Vendor the dependencies of a project or RockSpec locally.
340 /// When building or installing a package with the `--vendor-dir` option{n}
341 /// or the `[vendor_dir]` config option, Lux will fetch sources from the <vendor-dir>{n}
342 /// instead of from a remote server.
343 Vendor(Vendor),
344 /// Tell which file corresponds to a given module name.
345 Which(Which),
346 /// Spawns an interactive shell with PATH, LUA_PATH, LUA_CPATH and LUA_INIT set.
347 Shell(Shell),
348 /// Synchronize the project tree with the current lux.toml,{n}
349 /// ensuring all packages are installed correctly.
350 Sync(SyncProject),
351}
352
353/// Parse a key=value pair.
354fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
355where
356 T: std::str::FromStr,
357 T::Err: Error + Send + Sync + 'static,
358 U: std::str::FromStr,
359 U::Err: Error + Send + Sync + 'static,
360{
361 let pos = s
362 .find('=')
363 .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
364 Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
365}