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 clap::{Parser, Subcommand};
8use config::ConfigCmd;
9use debug::Debug;
10use doc::Doc;
11use download::Download;
12use exec::Exec;
13use generate_rockspec::GenerateRockspec;
14use info::Info;
15use install::Install;
16use install_rockspec::InstallRockspec;
17use lint::Lint;
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 completion;
39pub mod config;
40pub mod debug;
41pub mod doc;
42pub mod download;
43pub mod exec;
44pub mod fetch;
45pub mod format;
46pub mod generate_rockspec;
47pub mod info;
48pub mod install;
49pub mod install_lua;
50pub mod install_rockspec;
51pub mod lint;
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 /// Interact with the lux configuration.
153 #[command(subcommand, arg_required_else_help = true)]
154 Config(ConfigCmd),
155 /// Generate autocompletion scripts for the shell.{n}
156 /// Example: `lx completion zsh > ~/.zsh/completions/_lx`
157 Completion(Completion),
158 /// Internal commands for debugging Lux itself.
159 #[command(subcommand, arg_required_else_help = true)]
160 Debug(Debug),
161 /// Show documentation for an installed rock.
162 Doc(Doc),
163 /// Download a specific rock file from a luarocks server.
164 #[command(arg_required_else_help = true)]
165 Download(Download),
166 /// Formats the codebase with stylua.
167 Fmt(Fmt),
168 /// Generate a rockspec file from a project.
169 GenerateRockspec(GenerateRockspec),
170 /// Show metadata for any rock.
171 Info(Info),
172 /// Install a rock for use on the system.
173 #[command(arg_required_else_help = true)]
174 Install(Install),
175 /// Install a local rockspec for use on the system.
176 #[command(arg_required_else_help = true)]
177 InstallRockspec(InstallRockspec),
178 /// Manually install and manage Lua headers for various Lua versions.
179 InstallLua,
180 /// Lints the current project using `luacheck`.
181 Lint(Lint),
182 /// List currently installed rocks.
183 List(ListCmd),
184 /// Run lua, with the `LUA_PATH` and `LUA_CPATH` set to the specified lux tree.
185 Lua(RunLua),
186 /// Create a new Lua project.
187 New(NewProject),
188 /// List outdated rocks.
189 Outdated(Outdated),
190 /// Create a packed rock for distribution, packing sources or binaries.
191 Pack(Pack),
192 /// Return the currently configured package path.
193 Path(Path),
194 /// Pin an existing rock, preventing any updates to the package.
195 Pin(ChangePin),
196 /// Remove all installed rocks from a tree.
197 Purge,
198 /// Remove a rock from the current project's lux.toml dependencies.
199 Remove(Remove),
200 /// Run the current project with the provided arguments.
201 Run(Run),
202 /// Execute a command that has been installed with lux.
203 /// If the command is not found, a package named after the command
204 /// will be installed.
205 Exec(Exec),
206 /// Query the luarocks servers.
207 #[command(arg_required_else_help = true)]
208 Search(Search),
209 /// Run the test suite in the current project directory.{n}
210 /// Lux supports the following test backends, specified by the `[test]` table in the lux.toml:{n}
211 /// {n}
212 /// - busted:{n}
213 /// {n}
214 /// https://lunarmodules.github.io/busted/{n}
215 /// {n}
216 /// Example:{n}
217 /// {n}
218 /// ```toml{n}
219 /// [test]{n}
220 /// type = "busted"{n}
221 /// flags = [ ] # Optional CLI flags to pass to busted{n}
222 /// ```{n}
223 /// {n}
224 /// `lx test` will default to using `busted` if no test backend is specified and:{n}
225 /// * there is a `.busted` file in the project root{n}
226 /// * or `busted` is one of the `test_dependencies`).{n}
227 /// {n}
228 /// - busted-nlua:{n}:
229 /// {n}
230 /// [currently broken on macOS and Windows]
231 /// A build backend for running busted tests with Neovim as the Lua interpreter.
232 /// Used for testing Neovim plugins.
233 /// {n}
234 /// Example:{n}
235 /// {n}
236 /// ```toml{n}
237 /// [test]{n}
238 /// type = "busted-nlua"{n}
239 /// flags = [ ] # Optional CLI flags to pass to busted{n}
240 /// ```{n}
241 /// {n}
242 /// `lx test` will default to using `busted-nlua` if no test backend is specified and:{n}
243 /// * there is a `.busted` file in the project root{n}
244 /// * or `busted` and `nlua` are `test_dependencies`.{n}
245 /// {n}
246 /// - command:{n}
247 /// {n}
248 /// Name/file name of a shell command that will run the test suite.{n}
249 /// Example:{n}
250 /// {n}
251 /// ```toml{n}
252 /// [test]{n}
253 /// type = "command"{n}
254 /// command = "make"{n}
255 /// flags = [ "test" ]{n}
256 /// ```{n}
257 /// {n}
258 /// - script:{n}
259 /// {n}
260 /// Relative path to a Lua script that will run the test suite.{n}
261 /// Example:{n}
262 /// {n}
263 /// ```toml{n}
264 /// [test]{n}
265 /// type = "script"{n}
266 /// script = "tests.lua" # Expects a tests.lua file in the project root{n}
267 /// flags = [ ] # Optional arguments passed to the test script{n}
268 /// ```{n}
269 Test(Test),
270 /// Uninstall a rock from the system.
271 Uninstall(Uninstall),
272 /// Unpins an existing rock, allowing updates to alter the package.
273 Unpin(ChangePin),
274 /// Updates all rocks in a project.
275 Update(Update),
276 /// Generate a Lua rockspec for a Lux project and upload it to the public luarocks repository.{n}
277 /// You can specify a source template for release and dev packages in the lux.toml.{n}
278 /// {n}
279 /// Example:{n}
280 /// {n}
281 /// ```toml{n}
282 /// [source]{n}
283 /// url = "https://host.com/owner/$(PACKAGE)/refs/tags/$(REF).zip"{n}
284 /// dev = "git+https://host.com/owner/$(PACKAGE).git"{n}
285 /// ```{n}
286 /// {n}
287 /// You can use the following variables in the source template:{n}
288 /// {n}
289 /// - $(PACKAGE): The package name.{n}
290 /// - $(VERSION): The package version.{n}
291 /// - $(REF): The git tag or revision (if in a git repository).{n}
292 /// - You may also specify environment variables with `$(<VAR_NAME>)`.{n}
293 /// {n}
294 /// If the `version` is not set in the lux.toml, lux will search the current
295 /// commit for SemVer tags and if found, will use it to generate the package version.
296 Upload(Upload),
297 /// Tell which file corresponds to a given module name.
298 Which(Which),
299 /// Spawns an interactive shell with PATH, LUA_PATH, LUA_CPATH and LUA_INIT set.
300 Shell(Shell),
301}
302
303/// Parse a key=value pair.
304fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
305where
306 T: std::str::FromStr,
307 T::Err: Error + Send + Sync + 'static,
308 U: std::str::FromStr,
309 U::Err: Error + Send + Sync + 'static,
310{
311 let pos = s
312 .find('=')
313 .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
314 Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
315}