lux_cli/
lib.rs

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