rez_next_common/
config.rs1use serde::{Deserialize, Serialize};
4use std::env;
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct RezCoreConfig {
10 pub use_rust_version: bool,
12
13 pub use_rust_solver: bool,
15
16 pub use_rust_repository: bool,
18
19 pub rust_fallback: bool,
21
22 pub thread_count: Option<usize>,
24
25 pub cache: CacheConfig,
27
28 pub packages_path: Vec<String>,
30
31 pub local_packages_path: String,
33
34 pub release_packages_path: String,
36
37 pub default_shell: String,
39
40 pub version: String,
42
43 pub plugin_path: Vec<String>,
45
46 pub package_cache_path: Vec<String>,
48
49 pub tmpdir: String,
51
52 pub editor: String,
54
55 pub image_viewer: String,
57
58 pub browser: String,
60
61 pub difftool: String,
63
64 pub terminal_emulator_command: String,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct CacheConfig {
71 pub enable_memory_cache: bool,
73
74 pub enable_disk_cache: bool,
76
77 pub memory_cache_size: usize,
79
80 pub cache_ttl_seconds: u64,
82}
83
84impl RezCoreConfig {
85 pub fn new() -> Self {
86 Self::default()
87 }
88}
89
90impl Default for RezCoreConfig {
91 fn default() -> Self {
92 Self {
93 use_rust_version: true,
94 use_rust_solver: true,
95 use_rust_repository: true,
96 rust_fallback: true,
97 thread_count: None, cache: CacheConfig::default(),
99 packages_path: vec![
100 "~/packages".to_string(),
101 "~/.rez/packages/int".to_string(),
102 "~/.rez/packages/ext".to_string(),
103 ],
104 local_packages_path: "~/packages".to_string(),
105 release_packages_path: "~/.rez/packages/int".to_string(),
106 default_shell: if cfg!(windows) { "cmd" } else { "bash" }.to_string(),
107 version: env!("CARGO_PKG_VERSION").to_string(),
108 plugin_path: vec![],
109 package_cache_path: vec!["~/.rez/cache".to_string()],
110 tmpdir: std::env::temp_dir().to_string_lossy().to_string(),
111 editor: if cfg!(windows) { "notepad" } else { "vi" }.to_string(),
112 image_viewer: if cfg!(windows) { "mspaint" } else { "xdg-open" }.to_string(),
113 browser: if cfg!(windows) { "start" } else { "xdg-open" }.to_string(),
114 difftool: if cfg!(windows) { "fc" } else { "diff" }.to_string(),
115 terminal_emulator_command: if cfg!(windows) {
116 "cmd /c start cmd"
117 } else {
118 "xterm"
119 }
120 .to_string(),
121 }
122 }
123}
124
125impl Default for CacheConfig {
126 fn default() -> Self {
127 Self {
128 enable_memory_cache: true,
129 enable_disk_cache: true,
130 memory_cache_size: 1000,
131 cache_ttl_seconds: 3600, }
133 }
134}
135
136impl RezCoreConfig {
137 pub fn get_search_paths() -> Vec<PathBuf> {
139 let mut paths = Vec::new();
140
141 if let Ok(exe_path) = env::current_exe() {
143 if let Some(exe_dir) = exe_path.parent() {
144 paths.push(exe_dir.join("rezconfig.yaml"));
145 paths.push(exe_dir.join("rezconfig.json"));
146 }
147 }
148
149 if let Ok(config_file) = env::var("REZ_CONFIG_FILE") {
151 for path in config_file.split(std::path::MAIN_SEPARATOR) {
152 paths.push(PathBuf::from(path));
153 }
154 }
155
156 if cfg!(unix) {
158 paths.push(PathBuf::from("/etc/rez/config.yaml"));
159 paths.push(PathBuf::from("/usr/local/etc/rez/config.yaml"));
160 } else if cfg!(windows) {
161 if let Ok(program_data) = env::var("PROGRAMDATA") {
162 paths.push(PathBuf::from(program_data).join("rez").join("config.yaml"));
163 }
164 }
165
166 if env::var("REZ_DISABLE_HOME_CONFIG")
168 .unwrap_or_default()
169 .to_lowercase()
170 != "1"
171 {
172 if let Ok(home) = env::var("HOME") {
173 let home_path = PathBuf::from(&home);
174 paths.push(home_path.join(".rezconfig"));
175 paths.push(home_path.join(".rezconfig.yaml"));
176 paths.push(home_path.join(".rez").join("config.yaml"));
177 } else if cfg!(windows) {
178 if let Ok(userprofile) = env::var("USERPROFILE") {
179 let user_path = PathBuf::from(&userprofile);
180 paths.push(user_path.join(".rezconfig"));
181 paths.push(user_path.join(".rezconfig.yaml"));
182 paths.push(user_path.join(".rez").join("config.yaml"));
183 }
184 }
185 }
186
187 paths
188 }
189
190 pub fn get_sourced_paths() -> Vec<PathBuf> {
192 Self::get_search_paths()
193 .into_iter()
194 .filter(|path| path.exists())
195 .collect()
196 }
197
198 pub fn load() -> Self {
200 let mut config = Self::default();
201
202 for path in Self::get_search_paths() {
204 if path.exists() {
205 if let Ok(content) = std::fs::read_to_string(&path) {
206 if let Ok(loaded) = serde_yaml::from_str::<RezCoreConfig>(&content) {
208 config = loaded;
209 break;
210 }
211 if let Ok(loaded) = serde_json::from_str::<RezCoreConfig>(&content) {
213 config = loaded;
214 break;
215 }
216 }
217 }
218 }
219
220 if let Ok(packages_path) = env::var("REZ_PACKAGES_PATH") {
222 config.packages_path = packages_path.split(':').map(|s| s.to_string()).collect();
223 }
224 if let Ok(local_path) = env::var("REZ_LOCAL_PACKAGES_PATH") {
225 config.local_packages_path = local_path;
226 }
227 if let Ok(release_path) = env::var("REZ_RELEASE_PACKAGES_PATH") {
228 config.release_packages_path = release_path;
229 }
230
231 config
232 }
233
234 pub fn get_field(&self, field_path: &str) -> Option<serde_json::Value> {
236 let parts: Vec<&str> = field_path.split('.').collect();
237
238 let config_json = serde_json::to_value(self).ok()?;
240
241 let mut current = &config_json;
242 for part in parts {
243 current = current.get(part)?;
244 }
245
246 Some(current.clone())
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn test_default_config_has_sensible_values() {
256 let cfg = RezCoreConfig::default();
257 assert!(cfg.use_rust_solver);
258 assert!(cfg.use_rust_version);
259 assert!(cfg.use_rust_repository);
260 assert!(cfg.rust_fallback);
261 assert!(!cfg.packages_path.is_empty());
262 assert!(!cfg.local_packages_path.is_empty());
263 assert!(!cfg.release_packages_path.is_empty());
264 assert!(!cfg.version.is_empty());
265 }
266
267 #[test]
268 fn test_default_cache_config() {
269 let cfg = RezCoreConfig::default();
270 assert!(cfg.cache.enable_memory_cache);
271 assert!(cfg.cache.enable_disk_cache);
272 assert!(cfg.cache.memory_cache_size > 0);
273 assert!(cfg.cache.cache_ttl_seconds > 0);
274 }
275
276 #[test]
277 fn test_get_field_simple() {
278 let cfg = RezCoreConfig::default();
279 let v = cfg.get_field("version");
280 assert!(v.is_some());
281 if let Some(serde_json::Value::String(s)) = v {
282 assert!(!s.is_empty());
283 }
284 }
285
286 #[test]
287 fn test_get_field_packages_path() {
288 let cfg = RezCoreConfig::default();
289 let v = cfg.get_field("packages_path");
290 assert!(v.is_some());
291 if let Some(serde_json::Value::Array(arr)) = v {
292 assert!(!arr.is_empty());
293 }
294 }
295
296 #[test]
297 fn test_get_field_nested() {
298 let cfg = RezCoreConfig::default();
299 let v = cfg.get_field("cache.enable_memory_cache");
300 assert!(v.is_some());
301 assert_eq!(v, Some(serde_json::Value::Bool(true)));
302 }
303
304 #[test]
305 fn test_get_field_nested_numeric() {
306 let cfg = RezCoreConfig::default();
307 let v = cfg.get_field("cache.memory_cache_size");
308 assert!(v.is_some());
309 }
310
311 #[test]
312 fn test_get_field_nonexistent() {
313 let cfg = RezCoreConfig::default();
314 assert!(cfg.get_field("nonexistent_field").is_none());
315 assert!(cfg.get_field("cache.nonexistent").is_none());
316 }
317
318 #[test]
319 fn test_get_search_paths_not_empty() {
320 let paths = RezCoreConfig::get_search_paths();
321 assert!(!paths.is_empty());
322 }
323
324 #[test]
325 fn test_get_search_paths_contain_home_config() {
326 let paths = RezCoreConfig::get_search_paths();
327 let has_home = paths.iter().any(|p| {
328 p.to_string_lossy().contains(".rezconfig") || p.to_string_lossy().contains(".rez")
329 });
330 assert!(has_home);
331 }
332
333 #[test]
334 fn test_load_returns_config() {
335 let cfg = RezCoreConfig::load();
337 assert!(!cfg.version.is_empty());
338 }
339
340 #[test]
341 fn test_env_override_packages_path() {
342 if std::env::var("REZ_PACKAGES_PATH").is_err() {
344 std::env::set_var("REZ_PACKAGES_PATH", "/tmp/test_pkgs:/tmp/other_pkgs");
345 let cfg = RezCoreConfig::load();
346 assert!(cfg.packages_path.contains(&"/tmp/test_pkgs".to_string()));
347 std::env::remove_var("REZ_PACKAGES_PATH");
348 }
349 }
350
351 #[test]
352 fn test_config_serialization_roundtrip() {
353 let cfg = RezCoreConfig::default();
354 let json = serde_json::to_string(&cfg).unwrap();
355 let restored: RezCoreConfig = serde_json::from_str(&json).unwrap();
356 assert_eq!(cfg.version, restored.version);
357 assert_eq!(cfg.packages_path, restored.packages_path);
358 assert_eq!(
359 cfg.cache.memory_cache_size,
360 restored.cache.memory_cache_size
361 );
362 }
363
364 #[test]
365 fn test_config_clone() {
366 let cfg = RezCoreConfig::default();
367 let cloned = cfg.clone();
368 assert_eq!(cfg.version, cloned.version);
369 assert_eq!(cfg.packages_path, cloned.packages_path);
370 }
371}