1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Config {
9 #[serde(default = "default_listen")]
11 pub listen: String,
12
13 #[serde(default = "default_port")]
15 pub port: u16,
16
17 #[serde(default)]
19 pub servers: Vec<ServerConfig>,
20
21 #[serde(default = "default_log_level")]
23 pub log_level: String,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ServerConfig {
29 pub name: String,
31
32 pub url: String,
34
35 #[serde(default)]
37 pub transport: Transport,
38
39 #[serde(default = "default_timeout")]
41 pub timeout: u32,
42
43 #[serde(default)]
45 pub auth: Option<Auth>,
46}
47
48#[derive(Debug, Clone, Default, Serialize, Deserialize)]
50#[serde(rename_all = "lowercase")]
51pub enum Transport {
52 #[default]
53 Stdio,
54 Http,
55 WebSocket,
56 Zap,
57 Unix,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(tag = "type", rename_all = "lowercase")]
63pub enum Auth {
64 Bearer { token: String },
65 Basic { username: String, password: String },
66}
67
68fn default_listen() -> String {
69 "0.0.0.0".to_string()
70}
71
72fn default_port() -> u16 {
73 9999
74}
75
76fn default_timeout() -> u32 {
77 30000
78}
79
80fn default_log_level() -> String {
81 "info".to_string()
82}
83
84impl Default for Config {
85 fn default() -> Self {
86 Self {
87 listen: default_listen(),
88 port: default_port(),
89 servers: Vec::new(),
90 log_level: default_log_level(),
91 }
92 }
93}
94
95impl Config {
96 pub fn load(path: &PathBuf) -> crate::Result<Self> {
98 let content = std::fs::read_to_string(path)?;
99 let config: Config = toml::from_str(&content)
100 .map_err(|e| crate::Error::Config(e.to_string()))?;
101 Ok(config)
102 }
103
104 pub fn save(&self, path: &PathBuf) -> crate::Result<()> {
106 let content = toml::to_string_pretty(self)
107 .map_err(|e| crate::Error::Config(e.to_string()))?;
108 std::fs::write(path, content)?;
109 Ok(())
110 }
111
112 pub fn default_path() -> PathBuf {
114 directories::ProjectDirs::from("ai", "hanzo", "zap")
115 .map(|dirs| dirs.config_dir().join("config.toml"))
116 .unwrap_or_else(|| PathBuf::from("zap.toml"))
117 }
118}