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