use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(default = "default_listen")]
pub listen: String,
#[serde(default = "default_port")]
pub port: u16,
#[serde(default)]
pub servers: Vec<ServerConfig>,
#[serde(default = "default_log_level")]
pub log_level: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub name: String,
pub url: String,
#[serde(default)]
pub transport: Transport,
#[serde(default = "default_timeout")]
pub timeout: u32,
#[serde(default)]
pub auth: Option<Auth>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Transport {
#[default]
Stdio,
Http,
WebSocket,
Zap,
Unix,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum Auth {
Bearer { token: String },
Basic { username: String, password: String },
}
fn default_listen() -> String {
"0.0.0.0".to_string()
}
fn default_port() -> u16 {
9999
}
fn default_timeout() -> u32 {
30000
}
fn default_log_level() -> String {
"info".to_string()
}
impl Default for Config {
fn default() -> Self {
Self {
listen: default_listen(),
port: default_port(),
servers: Vec::new(),
log_level: default_log_level(),
}
}
}
impl Config {
pub fn load(path: &PathBuf) -> crate::Result<Self> {
let content = std::fs::read_to_string(path)?;
let config: Config = toml::from_str(&content)
.map_err(|e| crate::Error::Config(e.to_string()))?;
Ok(config)
}
pub fn save(&self, path: &PathBuf) -> crate::Result<()> {
let content = toml::to_string_pretty(self)
.map_err(|e| crate::Error::Config(e.to_string()))?;
std::fs::write(path, content)?;
Ok(())
}
pub fn default_path() -> PathBuf {
directories::ProjectDirs::from("ai", "hanzo", "zap")
.map(|dirs| dirs.config_dir().join("config.toml"))
.unwrap_or_else(|| PathBuf::from("zap.toml"))
}
}