Skip to main content

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