Skip to main content

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, enabling DEBUG logs.{n}
138    /// To enable TRACE logs, set RUST_LOG=trace.
139    #[arg(long)]
140    pub verbose: bool,
141
142    /// Don't print any progress bars or spinners.
143    #[arg(long)]
144    pub no_progress: bool,
145
146    /// Skip prompts, selecting the default option.
147    #[arg(long)]
148    pub no_prompt: bool,
149
150    /// Configure lux for installing Neovim packages.
151    #[arg(long)]
152    pub nvim: bool,
153
154    /// Timeout on network operations, in seconds.{n}
155    /// 0 means no timeout (wait forever). Default is 30.
156    #[arg(long, value_name = "seconds")]
157    pub timeout: Option<usize>,
158
159    /// Maximum buffer size for parallel jobs, such as downloading rockspecs and installing rocks.
160    /// 0 means no limit. Default is 0.
161    #[arg(long, visible_short_alias = 'j')]
162    pub max_jobs: Option<usize>,
163
164    /// Do not generate or update a `.luarc.json` file when building{n}
165    /// a project.
166    #[arg(long)]
167    pub no_luarc: bool,
168
169    /// Do not wrap Lua `bin` scripts.
170    #[arg(long)]
171    pub no_wrap_bin: bool,
172
173    /// The user agent to set when making web requests.
174    /// Default is "lux/<version>"
175    #[arg(long)]
176    pub user_agent: Option<String>,
177
178    #[command(subcommand)]
179    pub command: Commands,
180}
181
182#[derive(Subcommand)]
183pub enum Commands {
184    /// Add a dependency to the current project.
185    Add(Add),
186    /// Build/compile a project.
187    Build(Build),
188    /// [EXPERIMENTAL]{n}
189    /// Type check the current project based on EmmyLua/LuaCATS annotations.{n}
190    /// Respects `.emmyrc.json` and `.luarc.json` files in the project directory.
191    Check(Check),
192    /// Interact with the lux configuration.
193    #[command(subcommand, arg_required_else_help = true)]
194    Config(ConfigCmd),
195    /// Generate autocompletion scripts for the shell.{n}
196    /// Example: `lx completion zsh > ~/.zsh/completions/_lx`
197    Completion(Completion),
198    /// Internal commands for debugging Lux itself.
199    #[command(subcommand, arg_required_else_help = true)]
200    Debug(Debug),
201    /// Distribute a Lux project.
202    #[command(subcommand, arg_required_else_help = true)]
203    Dist(Dist),
204    /// Show documentation for an installed rock.
205    Doc(Doc),
206    /// Download a specific rock file from a luarocks server.
207    #[command(arg_required_else_help = true)]
208    Download(Download),
209    /// Formats the codebase with stylua.
210    Fmt(Fmt),
211    /// Generate a rockspec file from a project.
212    GenerateRockspec(GenerateRockspec),
213    /// Show metadata for any rock.
214    Info(Info),
215    /// Install a rock for use on the system.
216    #[command(arg_required_else_help = true)]
217    Install(Install),
218    /// Install a local rockspec for use on the system.
219    #[command(arg_required_else_help = true)]
220    InstallRockspec(InstallRockspec),
221    /// Manually install and manage Lua headers for various Lua versions.
222    InstallLua,
223    /// Lint the current project using `luacheck`.
224    Lint(Lint),
225    /// List currently installed rocks.
226    List(ListCmd),
227    /// Run lua, with the `LUA_PATH` and `LUA_CPATH` set to the specified lux tree.
228    Lua(RunLua),
229    /// Create a new Lua project.
230    New(NewProject),
231    /// List outdated rocks.
232    Outdated(Outdated),
233    /// Create a packed rock for distribution, packing sources or binaries.
234    Pack(Pack),
235    /// Return the currently configured package path.
236    Path(Path),
237    /// Pin an existing rock, preventing any updates to the package.
238    Pin(ChangePin),
239    /// Remove all installed rocks from a tree.
240    Purge,
241    /// Remove a rock from the current project's lux.toml dependencies.
242    Remove(Remove),
243    /// Run the current project with the provided arguments.
244    Run(Run),
245    /// Execute a command that has been installed with lux.
246    /// If the command is not found, a package named after the command
247    /// will be installed.
248    Exec(Exec),
249    /// Query the luarocks servers.
250    #[command(arg_required_else_help = true)]
251    Search(Search),
252    /// Run the test suite in the current project directory.{n}
253    /// Lux supports the following test backends, specified by the `[test]` table in the lux.toml:{n}
254    /// {n}
255    ///   - busted:{n}
256    ///     {n}
257    ///     https://lunarmodules.github.io/busted/{n}
258    ///     {n}
259    ///     Example:{n}
260    ///     {n}
261    ///     ```toml{n}
262    ///     [test]{n}
263    ///     type = "busted"{n}
264    ///     flags = [ ] # Optional CLI flags to pass to busted{n}
265    ///     ```{n}
266    ///     {n}
267    ///     `lx test` will default to using `busted` if no test backend is specified and:{n}
268    ///         * there is a `.busted` file in the project root{n}
269    ///         * or `busted` is one of the `test_dependencies`).{n}
270    /// {n}
271    ///   - busted-nlua:{n}:
272    ///     {n}
273    ///     [currently broken on Windows]{n}
274    ///     A build backend for running busted tests with Neovim as the Lua interpreter.
275    ///     Used for testing Neovim plugins.
276    ///     {n}
277    ///     Example:{n}
278    ///     {n}
279    ///     ```toml{n}
280    ///     [test]{n}
281    ///     type = "busted-nlua"{n}
282    ///     flags = [ ] # Optional CLI flags to pass to busted{n}
283    ///     ```{n}
284    ///     {n}
285    ///     `lx test` will default to using `busted-nlua` if no test backend is specified and:{n}
286    ///         * there is a `.busted` file in the project root{n}
287    ///         * or `busted` and `nlua` are `test_dependencies`.{n}
288    /// {n}
289    ///   - command:{n}
290    ///     {n}
291    ///     Name/file name of a shell command that will run the test suite.{n}
292    ///     Example:{n}
293    ///     {n}
294    ///     ```toml{n}
295    ///     [test]{n}
296    ///     type = "command"{n}
297    ///     command = "make"{n}
298    ///     flags = [ "test" ]{n}
299    ///     ```{n}
300    ///     {n}
301    ///   - script:{n}
302    ///     {n}
303    ///     Relative path to a Lua script that will run the test suite.{n}
304    ///     Example:{n}
305    ///     {n}
306    ///     ```toml{n}
307    ///     [test]{n}
308    ///     type = "script"{n}
309    ///     script = "tests.lua" # Expects a tests.lua file in the project root{n}
310    ///     flags = [ ] # Optional arguments passed to the test script{n}
311    ///     ```{n}
312    Test(Test),
313    /// Uninstall a rock from the system.
314    Uninstall(Uninstall),
315    /// Unpins an existing rock, allowing updates to alter the package.
316    Unpin(ChangePin),
317    /// Updates all rocks in a project.
318    Update(Update),
319    /// Generate a Lua rockspec for a Lux project and upload it to the public luarocks repository.{n}
320    /// You can specify a source template for release and dev packages in the lux.toml.{n}
321    /// {n}
322    /// Example:{n}
323    /// {n}
324    /// ```toml{n}
325    /// [source]{n}
326    /// url = "https://host.com/owner/$(PACKAGE)/refs/tags/$(REF).zip"{n}
327    /// dev = "git+https://host.com/owner/$(PACKAGE).git"{n}
328    /// ```{n}
329    /// {n}
330    /// You can use the following variables in the source template:{n}
331    /// {n}
332    ///  - $(PACKAGE): The package name.{n}
333    ///  - $(VERSION): The package version.{n}
334    ///  - $(REF): The git tag or revision (if in a git repository).{n}
335    ///  - You may also specify environment variables with `$(<VAR_NAME>)`.{n}
336    /// {n}
337    /// If the `version` is not set in the lux.toml, lux will search the current
338    /// commit for SemVer tags and if found, will use it to generate the package version.
339    Upload(Upload),
340    /// Vendor the dependencies of a project or RockSpec locally.
341    /// When building or installing a package with the `--vendor-dir` option{n}
342    /// or the `[vendor_dir]` config option, Lux will fetch sources from the <vendor-dir>{n}
343    /// instead of from a remote server.
344    Vendor(Vendor),
345    /// Tell which file corresponds to a given module name.
346    Which(Which),
347    /// Spawns an interactive shell with PATH, LUA_PATH, LUA_CPATH and LUA_INIT set.
348    Shell(Shell),
349    /// Synchronize the project tree with the current lux.toml,{n}
350    /// ensuring all packages are installed correctly.
351    Sync(SyncProject),
352}
353
354/// Parse a key=value pair.
355fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
356where
357    T: std::str::FromStr,
358    T::Err: Error + Send + Sync + 'static,
359    U: std::str::FromStr,
360    U::Err: Error + Send + Sync + 'static,
361{
362    let pos = s
363        .find('=')
364        .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
365    Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
366}