sps_common/
config.rs

1// sps-common/src/config.rs
2use std::env;
3use std::path::{Path, PathBuf};
4
5use directories::UserDirs; // Ensure this crate is in sps-common/Cargo.toml
6use tracing::debug;
7
8use super::error::Result; // Assuming SpsResult is Result from super::error
9
10// Make SPS_ROOT public
11pub const SPS_ROOT: &str = "/opt/sps";
12const SPS_ROOT_MARKER_FILENAME: &str = ".sps_root_v1";
13
14#[derive(Debug, Clone)]
15pub struct Config {
16    pub sps_root: PathBuf, // Public for direct construction in main for init if needed
17    pub api_base_url: String,
18    pub artifact_domain: Option<String>,
19    pub docker_registry_token: Option<String>,
20    pub docker_registry_basic_auth: Option<String>,
21    pub github_api_token: Option<String>,
22}
23
24impl Config {
25    pub fn load() -> Result<Self> {
26        debug!("Loading sps configuration");
27        let sps_root_path = PathBuf::from(SPS_ROOT);
28
29        let api_base_url = "https://formulae.brew.sh/api".to_string();
30
31        let artifact_domain = env::var("HOMEBREW_ARTIFACT_DOMAIN").ok();
32        let docker_registry_token = env::var("HOMEBREW_DOCKER_REGISTRY_TOKEN").ok();
33        let docker_registry_basic_auth = env::var("HOMEBREW_DOCKER_REGISTRY_BASIC_AUTH_TOKEN").ok();
34        let github_api_token = env::var("HOMEBREW_GITHUB_API_TOKEN").ok();
35
36        debug!("Configuration loaded successfully.");
37        Ok(Self {
38            sps_root: sps_root_path,
39            api_base_url,
40            artifact_domain,
41            docker_registry_token,
42            docker_registry_basic_auth,
43            github_api_token,
44        })
45    }
46
47    pub fn sps_root(&self) -> &Path {
48        &self.sps_root
49    }
50
51    pub fn bin_dir(&self) -> PathBuf {
52        self.sps_root.join("bin")
53    }
54
55    pub fn cellar_dir(&self) -> PathBuf {
56        self.sps_root.join("cellar")
57    }
58
59    pub fn cask_room_dir(&self) -> PathBuf {
60        self.sps_root.join("cask_room")
61    }
62
63    pub fn cask_store_dir(&self) -> PathBuf {
64        self.sps_root.join("cask_store")
65    }
66
67    pub fn opt_dir(&self) -> PathBuf {
68        self.sps_root.join("opt")
69    }
70
71    pub fn taps_dir(&self) -> PathBuf {
72        self.sps_root.join("taps")
73    }
74
75    pub fn cache_dir(&self) -> PathBuf {
76        self.sps_root.join("cache")
77    }
78
79    pub fn logs_dir(&self) -> PathBuf {
80        self.sps_root.join("logs")
81    }
82
83    pub fn tmp_dir(&self) -> PathBuf {
84        self.sps_root.join("tmp")
85    }
86
87    pub fn state_dir(&self) -> PathBuf {
88        self.sps_root.join("state")
89    }
90
91    pub fn man_base_dir(&self) -> PathBuf {
92        self.sps_root.join("share").join("man")
93    }
94
95    pub fn sps_root_marker_path(&self) -> PathBuf {
96        self.sps_root.join(SPS_ROOT_MARKER_FILENAME)
97    }
98
99    pub fn applications_dir(&self) -> PathBuf {
100        if cfg!(target_os = "macos") {
101            PathBuf::from("/Applications")
102        } else {
103            self.home_dir().join("Applications")
104        }
105    }
106
107    pub fn formula_cellar_dir(&self, formula_name: &str) -> PathBuf {
108        self.cellar_dir().join(formula_name)
109    }
110
111    pub fn formula_keg_path(&self, formula_name: &str, version_str: &str) -> PathBuf {
112        self.formula_cellar_dir(formula_name).join(version_str)
113    }
114
115    pub fn formula_opt_path(&self, formula_name: &str) -> PathBuf {
116        self.opt_dir().join(formula_name)
117    }
118
119    pub fn cask_room_token_path(&self, cask_token: &str) -> PathBuf {
120        self.cask_room_dir().join(cask_token)
121    }
122
123    pub fn cask_store_token_path(&self, cask_token: &str) -> PathBuf {
124        self.cask_store_dir().join(cask_token)
125    }
126
127    pub fn cask_store_version_path(&self, cask_token: &str, version_str: &str) -> PathBuf {
128        self.cask_store_token_path(cask_token).join(version_str)
129    }
130
131    pub fn cask_store_app_path(
132        &self,
133        cask_token: &str,
134        version_str: &str,
135        app_name: &str,
136    ) -> PathBuf {
137        self.cask_store_version_path(cask_token, version_str)
138            .join(app_name)
139    }
140
141    pub fn cask_room_version_path(&self, cask_token: &str, version_str: &str) -> PathBuf {
142        self.cask_room_token_path(cask_token).join(version_str)
143    }
144
145    pub fn home_dir(&self) -> PathBuf {
146        UserDirs::new().map_or_else(|| PathBuf::from("/"), |ud| ud.home_dir().to_path_buf())
147    }
148
149    pub fn get_tap_path(&self, name: &str) -> Option<PathBuf> {
150        let parts: Vec<&str> = name.split('/').collect();
151        if parts.len() == 2 {
152            Some(
153                self.taps_dir()
154                    .join(parts[0])
155                    .join(format!("homebrew-{}", parts[1])),
156            )
157        } else {
158            None
159        }
160    }
161
162    pub fn get_formula_path_from_tap(&self, tap_name: &str, formula_name: &str) -> Option<PathBuf> {
163        self.get_tap_path(tap_name).and_then(|tap_path| {
164            let json_path = tap_path
165                .join("Formula")
166                .join(format!("{formula_name}.json"));
167            if json_path.exists() {
168                return Some(json_path);
169            }
170            let rb_path = tap_path.join("Formula").join(format!("{formula_name}.rb"));
171            if rb_path.exists() {
172                return Some(rb_path);
173            }
174            None
175        })
176    }
177}
178
179impl Default for Config {
180    fn default() -> Self {
181        Self::load().expect("Failed to load default configuration")
182    }
183}
184
185// This function might be redundant if Config::default() is used,
186// but keeping it if it's called directly elsewhere.
187pub fn load_config() -> Result<Config> {
188    Config::load()
189}