rez_next_common/
config.rs

1//! Configuration management for rez-core
2
3#[cfg(feature = "python-bindings")]
4use pyo3::prelude::*;
5use serde::{Deserialize, Serialize};
6use std::env;
7use std::path::PathBuf;
8
9/// Configuration for rez-core components
10#[cfg_attr(feature = "python-bindings", pyclass(name = "Config"))]
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct RezCoreConfig {
13    /// Enable Rust version system
14    pub use_rust_version: bool,
15
16    /// Enable Rust solver
17    pub use_rust_solver: bool,
18
19    /// Enable Rust repository system
20    pub use_rust_repository: bool,
21
22    /// Fallback to Python on Rust errors
23    pub rust_fallback: bool,
24
25    /// Number of threads for parallel operations
26    pub thread_count: Option<usize>,
27
28    /// Cache configuration
29    pub cache: CacheConfig,
30
31    /// Package search paths
32    pub packages_path: Vec<String>,
33
34    /// Local packages path
35    pub local_packages_path: String,
36
37    /// Release packages path
38    pub release_packages_path: String,
39
40    /// Default shell
41    pub default_shell: String,
42
43    /// Rez version
44    pub version: String,
45
46    /// Plugin paths
47    pub plugin_path: Vec<String>,
48
49    /// Package cache paths
50    pub package_cache_path: Vec<String>,
51
52    /// Temporary directory
53    pub tmpdir: String,
54
55    /// Editor command
56    pub editor: String,
57
58    /// Image viewer command
59    pub image_viewer: String,
60
61    /// Browser command
62    pub browser: String,
63
64    /// Diff program
65    pub difftool: String,
66
67    /// Terminal type
68    pub terminal_emulator_command: String,
69}
70
71/// Cache configuration
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct CacheConfig {
74    /// Enable memory cache
75    pub enable_memory_cache: bool,
76
77    /// Enable disk cache
78    pub enable_disk_cache: bool,
79
80    /// Memory cache size (number of entries)
81    pub memory_cache_size: usize,
82
83    /// Cache TTL in seconds
84    pub cache_ttl_seconds: u64,
85}
86
87#[cfg(feature = "python-bindings")]
88#[pymethods]
89impl RezCoreConfig {
90    #[new]
91    pub fn new() -> Self {
92        Self::default()
93    }
94
95    pub fn __repr__(&self) -> String {
96        format!(
97            "Config(use_rust_version={}, use_rust_solver={}, use_rust_repository={})",
98            self.use_rust_version, self.use_rust_solver, self.use_rust_repository
99        )
100    }
101}
102
103#[cfg(not(feature = "python-bindings"))]
104impl RezCoreConfig {
105    pub fn new() -> Self {
106        Self::default()
107    }
108}
109
110impl Default for RezCoreConfig {
111    fn default() -> Self {
112        Self {
113            use_rust_version: true,
114            use_rust_solver: true,
115            use_rust_repository: true,
116            rust_fallback: true,
117            thread_count: None, // Use system default
118            cache: CacheConfig::default(),
119            packages_path: vec![
120                "~/packages".to_string(),
121                "~/.rez/packages/int".to_string(),
122                "~/.rez/packages/ext".to_string(),
123            ],
124            local_packages_path: "~/packages".to_string(),
125            release_packages_path: "~/.rez/packages/int".to_string(),
126            default_shell: if cfg!(windows) { "cmd" } else { "bash" }.to_string(),
127            version: env!("CARGO_PKG_VERSION").to_string(),
128            plugin_path: vec![],
129            package_cache_path: vec!["~/.rez/cache".to_string()],
130            tmpdir: std::env::temp_dir().to_string_lossy().to_string(),
131            editor: if cfg!(windows) { "notepad" } else { "vi" }.to_string(),
132            image_viewer: if cfg!(windows) { "mspaint" } else { "xdg-open" }.to_string(),
133            browser: if cfg!(windows) { "start" } else { "xdg-open" }.to_string(),
134            difftool: if cfg!(windows) { "fc" } else { "diff" }.to_string(),
135            terminal_emulator_command: if cfg!(windows) {
136                "cmd /c start cmd"
137            } else {
138                "xterm"
139            }
140            .to_string(),
141        }
142    }
143}
144
145impl Default for CacheConfig {
146    fn default() -> Self {
147        Self {
148            enable_memory_cache: true,
149            enable_disk_cache: true,
150            memory_cache_size: 1000,
151            cache_ttl_seconds: 3600, // 1 hour
152        }
153    }
154}
155
156impl RezCoreConfig {
157    /// Get the list of configuration file paths that are searched
158    pub fn get_search_paths() -> Vec<PathBuf> {
159        let mut paths = Vec::new();
160
161        // 1. Built-in config (if exists)
162        if let Ok(exe_path) = env::current_exe() {
163            if let Some(exe_dir) = exe_path.parent() {
164                paths.push(exe_dir.join("rezconfig.yaml"));
165                paths.push(exe_dir.join("rezconfig.json"));
166            }
167        }
168
169        // 2. Environment variable REZ_CONFIG_FILE
170        if let Ok(config_file) = env::var("REZ_CONFIG_FILE") {
171            for path in config_file.split(std::path::MAIN_SEPARATOR) {
172                paths.push(PathBuf::from(path));
173            }
174        }
175
176        // 3. System-wide config
177        if cfg!(unix) {
178            paths.push(PathBuf::from("/etc/rez/config.yaml"));
179            paths.push(PathBuf::from("/usr/local/etc/rez/config.yaml"));
180        } else if cfg!(windows) {
181            if let Ok(program_data) = env::var("PROGRAMDATA") {
182                paths.push(PathBuf::from(program_data).join("rez").join("config.yaml"));
183            }
184        }
185
186        // 4. User home config (unless disabled)
187        if env::var("REZ_DISABLE_HOME_CONFIG")
188            .unwrap_or_default()
189            .to_lowercase()
190            != "1"
191        {
192            if let Ok(home) = env::var("HOME") {
193                let home_path = PathBuf::from(&home);
194                paths.push(home_path.join(".rezconfig"));
195                paths.push(home_path.join(".rezconfig.yaml"));
196                paths.push(home_path.join(".rez").join("config.yaml"));
197            } else if cfg!(windows) {
198                if let Ok(userprofile) = env::var("USERPROFILE") {
199                    let user_path = PathBuf::from(&userprofile);
200                    paths.push(user_path.join(".rezconfig"));
201                    paths.push(user_path.join(".rezconfig.yaml"));
202                    paths.push(user_path.join(".rez").join("config.yaml"));
203                }
204            }
205        }
206
207        paths
208    }
209
210    /// Get the list of configuration files that actually exist and are sourced
211    pub fn get_sourced_paths() -> Vec<PathBuf> {
212        Self::get_search_paths()
213            .into_iter()
214            .filter(|path| path.exists())
215            .collect()
216    }
217
218    /// Load configuration from files
219    pub fn load() -> Self {
220        // For now, return default config
221        // TODO: Implement actual file loading with YAML/JSON parsing
222        Self::default()
223    }
224
225    /// Get a configuration field by dot-separated path
226    pub fn get_field(&self, field_path: &str) -> Option<serde_json::Value> {
227        let parts: Vec<&str> = field_path.split('.').collect();
228
229        // Convert config to JSON for easy field access
230        let config_json = serde_json::to_value(self).ok()?;
231
232        let mut current = &config_json;
233        for part in parts {
234            current = current.get(part)?;
235        }
236
237        Some(current.clone())
238    }
239}