Skip to main content

cc_core/
config.rs

1//! TOML 配置文件的数据结构与加载逻辑。
2//!
3//! 配置示例见 `config/config.toml.example`。
4
5use std::collections::HashMap;
6use std::path::Path;
7
8use serde::Deserialize;
9
10/// MySQL 连接名的抽象,用户可为枚举实现此 trait 以获得编译时检查。
11pub trait IntoMysqlName {
12    fn into_name(self) -> String;
13}
14
15/// Redis 连接名的抽象,用户可为枚举实现此 trait 以获得编译时检查。
16pub trait IntoRedisName {
17    fn into_name(self) -> String;
18}
19
20/// 为 String 实现,保持字符串方式可用。
21impl IntoMysqlName for String {
22    fn into_name(self) -> String {
23        self
24    }
25}
26
27/// 为 &str 实现,保持字符串方式可用。
28impl IntoMysqlName for &str {
29    fn into_name(self) -> String {
30        self.to_string()
31    }
32}
33
34/// 为 String 实现,保持字符串方式可用。
35impl IntoRedisName for String {
36    fn into_name(self) -> String {
37        self
38    }
39}
40
41/// 为 &str 实现,保持字符串方式可用。
42impl IntoRedisName for &str {
43    fn into_name(self) -> String {
44        self.to_string()
45    }
46}
47
48/// 单个 MySQL 连接的配置。
49#[derive(Debug, Clone, Deserialize)]
50pub struct MysqlConfig {
51    /// 主机名
52    pub host: String,
53    /// 端口
54    #[serde(default = "default_mysql_port")]
55    pub port: u16,
56    /// 用户名(兼容 `username` 写法)
57    #[serde(alias = "username")]
58    pub user: String,
59    /// 密码
60    pub password: String,
61    /// 默认库(schema);留空表示不指定
62    #[serde(default)]
63    pub database: String,
64    /// 连接池最大连接数
65    #[serde(default = "default_max_connections")]
66    pub max_connections: u32,
67    /// TLS 模式:disabled | preferred | required | verify-ca | verify-identity
68    #[serde(default = "default_ssl_mode")]
69    pub ssl_mode: String,
70}
71
72fn default_mysql_port() -> u16 {
73    3306
74}
75fn default_max_connections() -> u32 {
76    5
77}
78fn default_ssl_mode() -> String {
79    "preferred".to_string()
80}
81
82/// 单个 Redis 连接的配置。
83#[derive(Debug, Clone, Deserialize)]
84pub struct RedisConfig {
85    /// 形如 `redis://[:password@]host:port[/db]` 的连接串
86    pub url: String,
87}
88
89/// 整个配置文件:多个命名 MySQL / Redis 连接。
90#[derive(Debug, Clone, Default, Deserialize)]
91pub struct Config {
92    /// 名字 -> MySQL 配置
93    #[serde(default)]
94    pub mysql: HashMap<String, MysqlConfig>,
95    /// 名字 -> Redis 配置
96    #[serde(default)]
97    pub redis: HashMap<String, RedisConfig>,
98}
99
100impl Config {
101    /// 从 TOML 文件加载配置。
102    pub fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
103        let path = path.as_ref();
104        let text = std::fs::read_to_string(path)
105            .map_err(|e| anyhow::anyhow!("读取配置文件 {} 失败: {}", path.display(), e))?;
106        let cfg: Config = toml::from_str(&text)
107            .map_err(|e| anyhow::anyhow!("解析配置文件 {} 失败: {}", path.display(), e))?;
108        Ok(cfg)
109    }
110
111    /// 按名取 MySQL 配置。
112    pub fn mysql(&self, name: &str) -> Option<&MysqlConfig> {
113        self.mysql.get(name)
114    }
115
116    /// 按名取 Redis 配置。
117    pub fn redis(&self, name: &str) -> Option<&RedisConfig> {
118        self.redis.get(name)
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn parses_config() {
128        let toml = r#"
129            [mysql.default]
130            host = "h1"
131            port = 3306
132            user = "u1"
133            password = "p1"
134            database = "db1"
135            max_connections = 1
136            ssl_mode = "preferred"
137
138            [redis.default]
139            url = "redis://127.0.0.1:6379"
140        "#;
141        let cfg: Config = toml::from_str(toml).unwrap();
142
143        let d = cfg.mysql("default").unwrap();
144        assert_eq!(d.host, "h1");
145        assert_eq!(d.port, 3306);
146
147        assert_eq!(cfg.redis("default").unwrap().url, "redis://127.0.0.1:6379");
148    }
149}