use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolsConfig {
pub servers: HashMap<String, crate::common::ServerConfig>,
pub clients: HashMap<String, crate::common::ClientConfig>,
pub global: GlobalConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalConfig {
pub log_level: String,
pub enable_metrics: bool,
pub metrics_interval_secs: u64,
pub session_timeout_secs: u64,
}
impl Default for McpToolsConfig {
fn default() -> Self {
Self {
servers: HashMap::new(),
clients: HashMap::new(),
global: GlobalConfig::default(),
}
}
}
impl Default for GlobalConfig {
fn default() -> Self {
Self {
log_level: "info".to_string(),
enable_metrics: true,
metrics_interval_secs: 60,
session_timeout_secs: 3600, }
}
}
pub async fn load_config<P: AsRef<Path>>(path: P) -> Result<McpToolsConfig, Box<dyn std::error::Error + Send + Sync>> {
let content = tokio::fs::read_to_string(path).await?;
if let Ok(config) = toml::from_str::<McpToolsConfig>(&content) {
Ok(config)
} else {
serde_json::from_str::<McpToolsConfig>(&content)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
}
}
pub async fn save_config<P: AsRef<Path>>(config: &McpToolsConfig, path: P) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let content = toml::to_string_pretty(config)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
tokio::fs::write(path, content).await?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
#[test]
fn test_default_config() {
let config = McpToolsConfig::default();
assert_eq!(config.global.log_level, "info");
assert!(config.global.enable_metrics);
}
#[tokio::test]
async fn test_config_serialization() {
let config = McpToolsConfig::default();
let toml_str = toml::to_string(&config).unwrap();
let parsed: McpToolsConfig = toml::from_str(&toml_str).unwrap();
assert_eq!(config.global.log_level, parsed.global.log_level);
}
}