1use std::env;
10use std::fs;
11use std::path::{Path, PathBuf};
12
13use crate::Result;
14use crate::common::env_vars::FRM_DIR;
15use crate::errors::Error;
16use crate::version::Version;
17
18#[derive(Debug, Clone)]
19pub struct Paths {
20 base_dir: PathBuf,
21}
22
23impl Paths {
24 pub fn new() -> Result<Self> {
25 let base_dir = Self::detect_base_dir()?;
26 Ok(Self { base_dir })
27 }
28
29 pub fn with_base_dir(base_dir: PathBuf) -> Self {
30 Self { base_dir }
31 }
32
33 fn detect_base_dir() -> Result<PathBuf> {
34 if let Ok(dir) = env::var(FRM_DIR) {
35 return Ok(PathBuf::from(dir));
36 }
37
38 let home =
39 dirs::home_dir().ok_or_else(|| Error::Config("cannot find home directory".into()))?;
40
41 Ok(home.join(".local").join("frm"))
42 }
43
44 pub fn base_dir(&self) -> &Path {
45 &self.base_dir
46 }
47
48 pub fn versions_dir(&self) -> PathBuf {
49 self.base_dir.join("versions")
50 }
51
52 pub fn version_dir(&self, version: &Version) -> PathBuf {
53 self.versions_dir().join(version.dir_name())
54 }
55
56 pub fn version_sbin_dir(&self, version: &Version) -> PathBuf {
57 self.version_dir(version).join("sbin")
58 }
59
60 pub fn version_etc_dir(&self, version: &Version) -> PathBuf {
61 self.version_dir(version).join("etc").join("rabbitmq")
62 }
63
64 pub fn version_confd_dir(&self, version: &Version) -> PathBuf {
65 self.version_etc_dir(version).join("conf.d")
66 }
67
68 pub fn version_var_log_dir(&self, version: &Version) -> PathBuf {
69 self.version_dir(version)
70 .join("var")
71 .join("log")
72 .join("rabbitmq")
73 }
74
75 pub fn etc_dir(&self) -> PathBuf {
76 self.base_dir.join("etc").join("rabbitmq")
77 }
78
79 pub fn downloads_dir(&self) -> PathBuf {
80 self.base_dir.join("downloads")
81 }
82
83 pub fn config_file(&self) -> PathBuf {
84 self.base_dir.join("config.toml")
85 }
86
87 pub fn default_file(&self) -> PathBuf {
88 self.base_dir.join("default")
89 }
90
91 pub fn timestamps_file(&self) -> PathBuf {
92 self.base_dir.join("version_timestamps.json")
93 }
94
95 pub fn ensure_dirs(&self) -> Result<()> {
96 fs::create_dir_all(self.versions_dir())?;
97 fs::create_dir_all(self.downloads_dir())?;
98 fs::create_dir_all(self.etc_dir())?;
99 Ok(())
100 }
101
102 pub fn version_installed(&self, version: &Version) -> bool {
103 self.version_dir(version).exists()
104 }
105
106 pub fn installed_versions(&self) -> Result<Vec<Version>> {
107 let versions_dir = self.versions_dir();
108 if !versions_dir.exists() {
109 return Ok(Vec::new());
110 }
111
112 let mut versions = Vec::new();
113 for entry in fs::read_dir(versions_dir)? {
114 let entry = entry?;
115 if entry.file_type()?.is_dir()
116 && let Some(name) = entry.file_name().to_str()
117 && let Ok(version) = name.parse::<Version>()
118 {
119 versions.push(version);
120 }
121 }
122
123 versions.sort();
124 Ok(versions)
125 }
126
127 pub fn installed_alpha_versions(&self) -> Result<Vec<Version>> {
128 let versions = self.installed_versions()?;
129 Ok(versions
130 .into_iter()
131 .filter(|v| v.is_distributed_via_server_packages_repository())
132 .collect())
133 }
134
135 pub fn latest_ga_version(&self) -> Result<Option<Version>> {
136 let versions = self.installed_versions()?;
137 Ok(versions.into_iter().rev().find(|v| v.is_ga()))
138 }
139
140 pub fn latest_alpha_version(&self) -> Result<Option<Version>> {
141 let versions = self.installed_versions()?;
142 Ok(versions
143 .into_iter()
144 .rev()
145 .find(|v| v.is_distributed_via_server_packages_repository()))
146 }
147}
148
149impl Default for Paths {
150 fn default() -> Self {
151 Self::new().expect("failed to initialize paths")
152 }
153}