1use clap::{Arg, ArgAction, Command};
10use clap_complete::Shell as CompletionShell;
11
12use crate::commands::{CONFIG_FILES, RABBITMQ_TOOLS};
13use crate::shell::Shell;
14
15pub fn build_cli() -> Command {
16 Command::new("frm")
17 .version(env!("CARGO_PKG_VERSION"))
18 .author("Michael S. Klishin")
19 .about("Frakking RabbitMQ version Manager")
20 .arg_required_else_help(true)
21 .subcommand(releases_command())
22 .subcommand(alphas_command())
23 .subcommand(tanzu_command())
24 .subcommand(conf_command())
25 .subcommand(use_command())
26 .subcommand(default_command())
27 .subcommand(cli_command())
28 .subcommand(fg_command())
29 .subcommand(inspect_command())
30 .subcommand(env_command())
31 .subcommand(completions_command())
32}
33
34fn releases_command() -> Command {
35 Command::new("releases")
36 .about("Install or manage RabbitMQ releases (GA, RCs, betas); for alphas, see the 'alphas' command group")
37 .arg_required_else_help(true)
38 .subcommand(releases_list_command())
39 .subcommand(releases_path_command())
40 .subcommand(releases_logs_command())
41 .subcommand(releases_install_command())
42 .subcommand(releases_reinstall_command())
43 .subcommand(releases_uninstall_command())
44}
45
46fn releases_list_command() -> Command {
47 Command::new("list")
48 .visible_alias("ls")
49 .about("List installed stable RabbitMQ releases")
50}
51
52fn releases_path_command() -> Command {
53 Command::new("path")
54 .about("Show the local path of an installed release")
55 .long_about(
56 "Show the local path of an installed release.\n\n\
57 If no version is specified, tries to use the local .tool-versions file.",
58 )
59 .arg(version_arg())
60}
61
62fn releases_logs_command() -> Command {
63 Command::new("logs")
64 .about("Show RabbitMQ log file information for a release")
65 .arg_required_else_help(true)
66 .subcommand(
67 Command::new("path")
68 .about("Show the path to the log file")
69 .arg(version_arg()),
70 )
71 .subcommand(
72 Command::new("tail")
73 .about("Show the last lines of the log file")
74 .arg(version_arg())
75 .arg(
76 Arg::new("lines")
77 .long("lines")
78 .short('n')
79 .help("Number of lines to show")
80 .default_value("10")
81 .value_parser(clap::value_parser!(usize)),
82 ),
83 )
84}
85
86fn releases_install_command() -> Command {
87 Command::new("install")
88 .visible_alias("i")
89 .about("Install a stable RabbitMQ release")
90 .long_about(
91 "Install a stable RabbitMQ release (beta, rc, or GA).\n\n\
92 If no version is specified, tries to use the local .tool-versions file.\n\n\
93 Alpha versions are not allowed; use 'frm alphas install' instead.",
94 )
95 .arg(
96 Arg::new("version")
97 .help("Version to install (e.g., 4.2.3 or 4.2.0-rc.1)")
98 .index(1),
99 )
100 .arg(
101 Arg::new("force")
102 .long("force")
103 .short('f')
104 .help("Force reinstallation if version exists")
105 .action(ArgAction::SetTrue),
106 )
107}
108
109fn releases_reinstall_command() -> Command {
110 Command::new("reinstall")
111 .about("Reinstall a stable RabbitMQ release")
112 .long_about(
113 "Reinstall a stable RabbitMQ release.\n\n\
114 Removes the existing installation and downloads a fresh copy.\n\n\
115 If no version is specified, tries to use the local .tool-versions file.",
116 )
117 .arg(
118 Arg::new("version")
119 .help("Version to reinstall (e.g., 4.2.3)")
120 .index(1),
121 )
122}
123
124fn releases_uninstall_command() -> Command {
125 Command::new("uninstall")
126 .visible_alias("rm")
127 .about("Uninstall a stable RabbitMQ release")
128 .arg(
129 Arg::new("version")
130 .help("Version to uninstall (e.g., 4.2.3)")
131 .required(true)
132 .index(1),
133 )
134}
135
136fn alphas_command() -> Command {
137 Command::new("alphas")
138 .about("Install, manage, rotate alpha RabbitMQ releases")
139 .arg_required_else_help(true)
140 .subcommand(alphas_list_command())
141 .subcommand(alphas_path_command())
142 .subcommand(alphas_logs_command())
143 .subcommand(alphas_install_command())
144 .subcommand(alphas_reinstall_command())
145 .subcommand(alphas_uninstall_command())
146 .subcommand(alphas_prune_command())
147 .subcommand(alphas_clean_command())
148}
149
150fn alphas_list_command() -> Command {
151 Command::new("list")
152 .visible_alias("ls")
153 .about("List installed alpha RabbitMQ releases")
154}
155
156fn alphas_path_command() -> Command {
157 Command::new("path")
158 .about("Show the local path of an installed alpha release")
159 .long_about(
160 "Show the local path of an installed alpha release.\n\n\
161 If no version is specified, tries to use the local .tool-versions file.",
162 )
163 .arg(version_arg())
164}
165
166fn alphas_logs_command() -> Command {
167 Command::new("logs")
168 .about("Show RabbitMQ log file information for an alpha release")
169 .arg_required_else_help(true)
170 .subcommand(
171 Command::new("path")
172 .about("Show the path to the log file")
173 .arg(version_arg()),
174 )
175 .subcommand(
176 Command::new("tail")
177 .about("Show the last lines of the log file")
178 .arg(version_arg())
179 .arg(
180 Arg::new("lines")
181 .long("lines")
182 .short('n')
183 .help("Number of lines to show")
184 .default_value("10")
185 .value_parser(clap::value_parser!(usize)),
186 ),
187 )
188}
189
190fn alphas_install_command() -> Command {
191 Command::new("install")
192 .visible_alias("i")
193 .about("Install an alpha RabbitMQ release")
194 .long_about(
195 "Install an alpha RabbitMQ release from rabbitmq/server-packages.\n\n\
196 Use --latest to automatically install the most recent alpha release.",
197 )
198 .arg(
199 Arg::new("version")
200 .help("Alpha version to install (e.g., 4.3.0-alpha.132057c7)")
201 .index(1)
202 .conflicts_with("latest"),
203 )
204 .arg(
205 Arg::new("latest")
206 .long("latest")
207 .short('l')
208 .help("Install the most recent alpha release")
209 .action(ArgAction::SetTrue),
210 )
211 .arg(
212 Arg::new("force")
213 .long("force")
214 .short('f')
215 .help("Force reinstallation if version exists")
216 .action(ArgAction::SetTrue),
217 )
218}
219
220fn alphas_reinstall_command() -> Command {
221 Command::new("reinstall")
222 .about("Reinstall an alpha RabbitMQ release")
223 .long_about(
224 "Reinstall an alpha RabbitMQ release.\n\n\
225 Removes the existing installation and downloads a fresh copy.",
226 )
227 .arg(
228 Arg::new("version")
229 .help("Alpha version to reinstall (e.g., 4.3.0-alpha.132057c7)")
230 .required(true)
231 .index(1),
232 )
233}
234
235fn alphas_uninstall_command() -> Command {
236 Command::new("uninstall")
237 .visible_alias("rm")
238 .about("Uninstall an alpha RabbitMQ release")
239 .arg(
240 Arg::new("version")
241 .help("Alpha version to uninstall (e.g., 4.3.0-alpha.132057c7)")
242 .required(true)
243 .index(1),
244 )
245}
246
247fn alphas_prune_command() -> Command {
248 Command::new("prune")
249 .about("Remove all installed alpha releases")
250 .long_about("Remove all installed alpha releases to free up disk space.")
251}
252
253fn alphas_clean_command() -> Command {
254 Command::new("clean")
255 .about("Remove alpha releases older than a specified time")
256 .long_about(
257 "Remove alpha releases older than a specified time.\n\n\
258 The --older-than flag accepts human-readable time strings like:\n\
259 - \"2 weeks ago\"\n\
260 - \"1 month ago\"\n\
261 - \"yesterday\"\n\
262 - \"2025-01-01\" (absolute date)",
263 )
264 .arg(
265 Arg::new("older_than")
266 .long("older-than")
267 .help("Remove alphas installed before this time (e.g., \"2 weeks ago\")")
268 .required(true)
269 .value_name("TIME"),
270 )
271}
272
273fn tanzu_command() -> Command {
274 Command::new("tanzu")
275 .about("Install Tanzu RabbitMQ from local tarballs")
276 .arg_required_else_help(true)
277 .subcommand(tanzu_install_command())
278}
279
280fn tanzu_install_command() -> Command {
281 Command::new("install")
282 .visible_alias("i")
283 .about("Install Tanzu RabbitMQ from a local tarball")
284 .long_about(
285 "Install Tanzu RabbitMQ from a local tarball.\n\n\
286 Requires both the tarball path and the expected version.\n\
287 The version in the tarball filename must match the specified version.\n\n\
288 Supported formats: .tar.xz, .tar.gz, .tgz",
289 )
290 .arg(
291 Arg::new("tarball_path")
292 .long("local-tanzu-rabbitmq-tarball-path")
293 .help("Path to the local Tanzu RabbitMQ tarball")
294 .required(true)
295 .value_name("PATH"),
296 )
297 .arg(
298 Arg::new("version")
299 .long("version")
300 .short('V')
301 .help("Expected RabbitMQ version (e.g., 4.2.3 or 4.2.3-rc.1)")
302 .required(true)
303 .value_name("VERSION"),
304 )
305 .arg(
306 Arg::new("force")
307 .long("force")
308 .short('f')
309 .help("Force reinstallation if version exists")
310 .action(ArgAction::SetTrue),
311 )
312}
313
314fn conf_command() -> Command {
315 Command::new("conf")
316 .about("Manage RabbitMQ configuration files")
317 .arg_required_else_help(true)
318 .subcommand(conf_get_key_command())
319 .subcommand(conf_set_key_command())
320}
321
322fn conf_get_key_command() -> Command {
323 Command::new("get-key")
324 .about("Get a configuration key value from rabbitmq.conf")
325 .long_about(
326 "Get a configuration key value from rabbitmq.conf.\n\n\
327 Supports pattern matching with * as a wildcard for a single segment:\n\n \
328 * `listeners.tcp.*` matches `listeners.tcp.default`, `listeners.tcp.amqp`, etc.\n \
329 * `log.*.level` matches `log.console.level`, `log.file.level`, etc.\n\n\
330 If no version is specified, tries to use the local .tool-versions file.",
331 )
332 .arg(
333 Arg::new("key")
334 .help("Configuration key or pattern (e.g., listeners.tcp.* or heartbeat)")
335 .required(true)
336 .index(1),
337 )
338 .arg(version_arg())
339}
340
341fn conf_set_key_command() -> Command {
342 Command::new("set-key")
343 .about("Set a configuration key value in rabbitmq.conf")
344 .long_about(
345 "Set a configuration key value in rabbitmq.conf.\n\n\
346 If no version is specified, tries to use the local .tool-versions file.\n\n\
347 Keys are validated against the known RabbitMQ configuration schema.\n\
348 Use --force to set unknown keys.",
349 )
350 .arg(
351 Arg::new("key")
352 .help("Configuration key (e.g., listeners.tcp.default)")
353 .required(true)
354 .index(1),
355 )
356 .arg(
357 Arg::new("value")
358 .help("Value to set")
359 .required(true)
360 .index(2),
361 )
362 .arg(version_arg())
363 .arg(
364 Arg::new("force")
365 .long("force")
366 .short('f')
367 .help("Set the key even if it's not recognized")
368 .action(ArgAction::SetTrue),
369 )
370}
371
372fn use_command() -> Command {
373 Command::new("use")
374 .about("Output shell commands to use a specific version")
375 .long_about(
376 "Output shell commands to use a specific version.\n\n\
377 If no version is specified, tries to use the local .tool-versions file.\n\n\
378 bash/zsh: eval \"$(frm use [version])\"\n\
379 nushell: Use 'frm env nu' init script, then call 'frm-use [version]'",
380 )
381 .arg(
382 Arg::new("version")
383 .help("Version to use (e.g., 4.2.3), or uses .tool-versions")
384 .index(1),
385 )
386 .arg(
387 Arg::new("shell")
388 .long("shell")
389 .short('s')
390 .help("Shell type (bash, zsh, nu)")
391 .value_parser(clap::value_parser!(Shell)),
392 )
393}
394
395fn default_command() -> Command {
396 Command::new("default")
397 .about("Set the default RabbitMQ version")
398 .arg(
399 Arg::new("version")
400 .help("Version to set as default (e.g., 4.2.3)")
401 .required(true)
402 .index(1),
403 )
404}
405
406fn env_command() -> Command {
407 Command::new("env")
408 .about("Output shell initialization script")
409 .long_about(
410 "Output shell initialization script.\n\n\
411 Add to your shell profile:\n\
412 - bash: eval \"$(frm env bash)\" in ~/.bashrc\n\
413 - zsh: eval \"$(frm env zsh)\" in ~/.zshrc\n\
414 - nu: frm env nu | save -f ~/.local/frm/env.nu, then source in config.nu\n\n\
415 After setup, use 'frm-use <version>' to switch versions.",
416 )
417 .arg(
418 Arg::new("shell")
419 .help("Shell type (bash, zsh, nu)")
420 .required(true)
421 .index(1)
422 .value_parser(clap::value_parser!(Shell)),
423 )
424}
425
426fn cli_command() -> Command {
427 Command::new("cli")
428 .about("Run a RabbitMQ CLI tool")
429 .long_about(format!(
430 "Run a RabbitMQ CLI tool from the specified version.\n\n\
431 Available tools: {}\n\n\
432 If no version is specified, tries to use the local .tool-versions file.\n\n\
433 Use -- to separate tool arguments from frm options:\n\
434 frm cli rabbitmqctl -V 4.2.3 -- status",
435 RABBITMQ_TOOLS.join(", ")
436 ))
437 .trailing_var_arg(true)
438 .arg(Arg::new("tool").help("Tool to run").required(true).index(1))
439 .arg(version_arg())
440 .arg(
441 Arg::new("args")
442 .help("Arguments to pass to the tool (after --)")
443 .num_args(1..)
444 .index(2),
445 )
446}
447
448fn fg_command() -> Command {
449 Command::new("fg")
450 .about("Run RabbitMQ nodes in foreground")
451 .arg_required_else_help(true)
452 .subcommand(
453 Command::new("node")
454 .about("Start RabbitMQ server in foreground")
455 .long_about(
456 "Start RabbitMQ server in foreground.\n\n\
457 If no version is specified, tries to use the local .tool-versions file.",
458 )
459 .arg(version_arg()),
460 )
461}
462
463fn inspect_command() -> Command {
464 Command::new("inspect")
465 .about("Inspect a RabbitMQ configuration file")
466 .long_about(format!(
467 "Inspect a RabbitMQ configuration file from the specified version.\n\n\
468 Available files: {}\n\n\
469 If no version is specified, tries to use the local .tool-versions file.",
470 CONFIG_FILES.join(", ")
471 ))
472 .arg(
473 Arg::new("file")
474 .help("Configuration file to inspect")
475 .required(true)
476 .index(1),
477 )
478 .arg(version_arg())
479}
480
481fn version_arg() -> Arg {
482 Arg::new("version")
483 .long("version")
484 .short('V')
485 .help("RabbitMQ version to use, or uses .tool-versions")
486 .value_name("VERSION")
487}
488
489fn completions_command() -> Command {
490 Command::new("completions")
491 .about("Generate shell completions")
492 .arg(
493 Arg::new("shell")
494 .help("Target shell")
495 .required(true)
496 .index(1)
497 .value_parser(clap::value_parser!(CompletionShell)),
498 )
499}