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