use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::error::WebError;
use super::models::{McpConfigFile, McpServerConfig};
pub(super) fn global_config_path() -> PathBuf {
let home = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.unwrap_or_else(|_| "/tmp".to_string());
PathBuf::from(home).join(".opendev").join("mcp.json")
}
pub(super) fn project_config_path(working_dir: &str) -> PathBuf {
PathBuf::from(working_dir).join(".opendev").join("mcp.json")
}
pub(super) fn load_all_servers(working_dir: &str) -> HashMap<String, McpServerConfig> {
let mut servers = HashMap::new();
let global_path = global_config_path();
if let Ok(content) = std::fs::read_to_string(&global_path)
&& let Ok(config) = serde_json::from_str::<McpConfigFile>(&content)
{
servers.extend(config.mcp_servers);
}
let project_path = project_config_path(working_dir);
if let Ok(content) = std::fs::read_to_string(&project_path)
&& let Ok(config) = serde_json::from_str::<McpConfigFile>(&content)
{
servers.extend(config.mcp_servers);
}
servers
}
pub(super) fn save_server_to_config(
name: &str,
config: &McpServerConfig,
config_path: &Path,
) -> Result<(), WebError> {
if let Some(parent) = config_path.parent() {
std::fs::create_dir_all(parent)
.map_err(|e| WebError::Internal(format!("Failed to create config directory: {}", e)))?;
}
let mut mcp_config = if let Ok(content) = std::fs::read_to_string(config_path) {
serde_json::from_str::<McpConfigFile>(&content).unwrap_or(McpConfigFile {
mcp_servers: HashMap::new(),
})
} else {
McpConfigFile {
mcp_servers: HashMap::new(),
}
};
mcp_config
.mcp_servers
.insert(name.to_string(), config.clone());
let content = serde_json::to_string_pretty(&mcp_config)
.map_err(|e| WebError::Internal(format!("Failed to serialize config: {}", e)))?;
std::fs::write(config_path, content)
.map_err(|e| WebError::Internal(format!("Failed to write config: {}", e)))?;
Ok(())
}
pub(super) fn remove_server_from_config(name: &str, config_path: &Path) -> Result<bool, WebError> {
if !config_path.exists() {
return Ok(false);
}
let content = std::fs::read_to_string(config_path)
.map_err(|e| WebError::Internal(format!("Failed to read config: {}", e)))?;
let mut mcp_config = serde_json::from_str::<McpConfigFile>(&content)
.map_err(|e| WebError::Internal(format!("Failed to parse config: {}", e)))?;
let removed = mcp_config.mcp_servers.remove(name).is_some();
if removed {
let content = serde_json::to_string_pretty(&mcp_config)
.map_err(|e| WebError::Internal(format!("Failed to serialize config: {}", e)))?;
std::fs::write(config_path, content)
.map_err(|e| WebError::Internal(format!("Failed to write config: {}", e)))?;
}
Ok(removed)
}
#[cfg(test)]
#[path = "io_tests.rs"]
mod tests;