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