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