1use std::env;
10use std::io::{self, Write};
11
12use crate::Result;
13use crate::common::env_vars::RABBITMQ_HOME;
14use crate::config::Config;
15use crate::paths::Paths;
16use crate::version::Version;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Status {
20 pub active: Option<Version>,
21 pub default: Option<Version>,
22 pub releases: Vec<Version>,
23 pub alphas: Vec<Version>,
24}
25
26impl Status {
27 pub fn collect(paths: &Paths) -> Result<Self> {
28 let active = detect_active_version(paths);
29 let config = Config::load(paths)?;
30 let default = config.default_version;
31
32 let all_versions = paths.installed_versions()?;
33 let (alphas, releases): (Vec<_>, Vec<_>) = all_versions
34 .into_iter()
35 .partition(|v| v.is_distributed_via_server_packages_repository());
36
37 Ok(Self {
38 active,
39 default,
40 releases,
41 alphas,
42 })
43 }
44
45 pub fn format(&self) -> String {
46 let mut out = String::new();
47
48 match (&self.active, &self.default) {
49 (Some(active), Some(default)) if active == default => {
50 out.push_str(&format!("Active: {} (default)\n", active));
51 }
52 (Some(active), Some(default)) => {
53 out.push_str(&format!("Active: {}\n", active));
54 out.push_str(&format!("Default: {}\n", default));
55 }
56 (Some(active), None) => {
57 out.push_str(&format!("Active: {}\n", active));
58 }
59 (None, Some(default)) => {
60 out.push_str(&format!("Default: {}\n", default));
61 }
62 (None, None) => {}
63 }
64
65 if self.releases.is_empty() && self.alphas.is_empty() {
66 if out.is_empty() {
67 out.push_str("No RabbitMQ versions installed\n");
68 }
69 return out;
70 }
71
72 if !out.is_empty() {
73 out.push('\n');
74 }
75
76 out.push_str("Installed:\n\n");
77
78 for version in self.releases.iter().rev() {
79 let marker = self.version_marker(version);
80 out.push_str(&format!(" {} {}\n", marker, version));
81 }
82
83 for version in self.alphas.iter().rev() {
84 let marker = self.version_marker(version);
85 out.push_str(&format!(" {} {}\n", marker, version));
86 }
87
88 out
89 }
90
91 fn version_marker(&self, version: &Version) -> &'static str {
92 let is_active = self.active.as_ref() == Some(version);
93 let is_default = self.default.as_ref() == Some(version);
94
95 match (is_active, is_default) {
96 (true, _) => "🟢",
97 (false, true) => "⚪",
98 (false, false) => " ",
99 }
100 }
101}
102
103fn detect_active_version(paths: &Paths) -> Option<Version> {
104 let rabbitmq_home = env::var(RABBITMQ_HOME).ok()?;
105 let versions_dir = paths.versions_dir();
106 let versions_prefix = versions_dir.to_string_lossy();
107
108 if !rabbitmq_home.starts_with(versions_prefix.as_ref()) {
109 return None;
110 }
111
112 let version_str = rabbitmq_home
113 .strip_prefix(versions_prefix.as_ref())?
114 .trim_start_matches('/');
115
116 version_str.parse().ok()
117}
118
119pub fn run(paths: &Paths) -> Result<()> {
120 let status = Status::collect(paths)?;
121 let output = status.format();
122 io::stdout().write_all(output.as_bytes())?;
123 Ok(())
124}