use std::path::{Path, PathBuf};
use tracing::{debug, info};
use crate::error::{OrchestratorError, Result};
use super::defaults::PROJECT_CONFIG_FILE;
use super::jsonc;
use super::types::OrchestratorConfig;
use super::get_global_config_paths;
const RETIRED_KEYS: [(&str, &str); 1] = [(
"parallel_mode",
"cumulative Git-worktree orchestration is the only execution model; \
remove \"parallel_mode\" from your Conflux configuration",
)];
fn reject_retired_keys(document: &str) -> Result<()> {
let Ok(serde_json::Value::Object(map)) = serde_json::from_str::<serde_json::Value>(document)
else {
return Ok(());
};
for (key, guidance) in RETIRED_KEYS {
if map.contains_key(key) {
return Err(OrchestratorError::ConfigParse(format!(
"retired configuration key \"{key}\": {guidance}"
)));
}
}
Ok(())
}
impl OrchestratorConfig {
pub fn load_from_file(path: &Path) -> Result<Self> {
let content = std::fs::read_to_string(path).map_err(|e| {
OrchestratorError::ConfigLoad(format!("Failed to read config file {:?}: {}", path, e))
})?;
Self::parse_jsonc(&content).map_err(|err| match err {
OrchestratorError::ConfigParse(msg) => OrchestratorError::ConfigParse(format!(
"Failed to parse config file {:?}: {}",
path, msg
)),
other => other,
})
}
pub fn parse_jsonc(content: &str) -> Result<Self> {
let document = jsonc::strip_jsonc_features(content);
reject_retired_keys(&document)?;
jsonc::parse(content)
}
pub fn load(custom_path: Option<&Path>) -> Result<Self> {
let config = Self::merge_all_sources(custom_path)?;
config.validate_required_commands()?;
info!("Configuration loaded and merged successfully");
Ok(config)
}
pub fn load_storage_settings(custom_path: Option<&Path>) -> Result<Self> {
Self::merge_all_sources(custom_path)
}
fn merge_all_sources(custom_path: Option<&Path>) -> Result<Self> {
let mut config = Self::default();
for path in get_global_config_paths() {
if path.exists() {
debug!("Loading global config from: {:?}", path);
let loaded_config = Self::load_from_file(&path)?;
config.merge(loaded_config);
}
}
let project_config_path = PathBuf::from(PROJECT_CONFIG_FILE);
if project_config_path.exists() {
debug!("Loading project config from: {:?}", project_config_path);
let project_config = Self::load_from_file(&project_config_path)?;
config.merge(project_config);
}
if let Some(path) = custom_path {
debug!("Loading custom config from: {:?}", path);
let custom_config = Self::load_from_file(path)?;
config.merge(custom_config);
}
Ok(config)
}
}