Skip to main content

dm_database_sqllog2db/
config.rs

1use crate::constants::LOG_LEVELS;
2use crate::error::{ConfigError, Error, Result};
3pub use crate::features::{FeaturesConfig, FiltersFeature};
4use serde::Deserialize;
5use std::path::{Path, PathBuf};
6
7/// 默认表名
8#[cfg(feature = "sqlite")]
9fn default_table_name() -> String {
10    "sqllog_records".to_string()
11}
12
13/// 默认 true 值
14fn default_true() -> bool {
15    true
16}
17
18#[cfg_attr(feature = "csv", derive(Default))]
19#[derive(Debug, Deserialize, Clone)]
20pub struct Config {
21    /// 新增:SQL 日志输入相关配置
22    #[serde(default)]
23    pub sqllog: SqllogConfig,
24    #[serde(default)]
25    pub error: ErrorConfig,
26    #[serde(default)]
27    pub logging: LoggingConfig,
28    #[serde(default)]
29    pub features: FeaturesConfig,
30    #[serde(default)]
31    pub exporter: ExporterConfig,
32}
33
34impl Config {
35    /// 从文件加载配置
36    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
37        let path = path.as_ref();
38        let content = std::fs::read_to_string(path)
39            .map_err(|_| Error::Config(ConfigError::NotFound(path.to_path_buf())))?;
40        Self::from_str(&content, path.to_path_buf())
41    }
42
43    /// 从字符串解析配置
44    pub fn from_str(content: &str, path: PathBuf) -> Result<Self> {
45        let config: Config = toml::from_str(content).map_err(|e| {
46            Error::Config(ConfigError::ParseFailed {
47                path,
48                reason: e.to_string(),
49            })
50        })?;
51
52        // 验证配置
53        config.validate()?;
54
55        Ok(config)
56    }
57
58    /// 验证配置的有效性
59    pub fn validate(&self) -> Result<()> {
60        // 验证日志级别
61        self.logging.validate()?;
62
63        // 验证导出器配置
64        self.exporter.validate()?;
65
66        // 验证 sqllog 配置
67        self.sqllog.validate()?;
68
69        // 验证功能配置
70        FeaturesConfig::validate();
71
72        Ok(())
73    }
74}
75
76/// SQL 日志输入配置
77#[derive(Debug, Deserialize, Clone)]
78pub struct SqllogConfig {
79    /// SQL 日志输入目录(可包含多个日志文件)
80    pub directory: String,
81}
82
83impl Default for SqllogConfig {
84    fn default() -> Self {
85        Self {
86            directory: "sqllogs".to_string(),
87        }
88    }
89}
90
91impl SqllogConfig {
92    /// 获取 SQL 日志输入目录
93    #[must_use]
94    pub fn directory(&self) -> &str {
95        &self.directory
96    }
97
98    /// 验证配置
99    pub fn validate(&self) -> Result<()> {
100        if self.directory.trim().is_empty() {
101            return Err(Error::Config(ConfigError::InvalidValue {
102                field: "sqllog.directory".to_string(),
103                value: self.directory.clone(),
104                reason: "Input directory cannot be empty".to_string(),
105            }));
106        }
107        Ok(())
108    }
109}
110
111#[derive(Debug, Deserialize, Clone)]
112pub struct ErrorConfig {
113    /// 错误日志输出文件路径
114    #[serde(default = "default_error_file")]
115    pub file: String,
116}
117
118fn default_error_file() -> String {
119    "export/errors.log".to_string()
120}
121
122impl ErrorConfig {
123    /// 获取错误日志输出文件路径
124    #[must_use]
125    pub fn file(&self) -> &str {
126        &self.file
127    }
128}
129
130impl Default for ErrorConfig {
131    fn default() -> Self {
132        Self {
133            file: "export/errors.log".to_string(),
134        }
135    }
136}
137
138#[derive(Debug, Deserialize, Clone)]
139pub struct LoggingConfig {
140    /// 应用日志输出文件路径
141    #[serde(default = "default_logging_file")]
142    pub file: String,
143    #[serde(default = "default_logging_level")]
144    pub level: String,
145    #[serde(default = "default_retention_days")]
146    pub retention_days: usize,
147}
148
149fn default_logging_file() -> String {
150    "logs/sqllog2db.log".to_string()
151}
152
153fn default_logging_level() -> String {
154    "info".to_string()
155}
156
157fn default_retention_days() -> usize {
158    7
159}
160
161impl LoggingConfig {
162    /// 获取日志输出文件路径
163    #[must_use]
164    pub fn file(&self) -> &str {
165        &self.file
166    }
167
168    /// 获取日志级别
169    #[must_use]
170    pub fn level(&self) -> &str {
171        &self.level
172    }
173
174    /// 获取日志保留天数
175    #[must_use]
176    pub fn retention_days(&self) -> usize {
177        self.retention_days
178    }
179
180    /// 验证日志级别是否有效
181    pub fn validate(&self) -> Result<()> {
182        if !LOG_LEVELS
183            .iter()
184            .any(|&l| l.eq_ignore_ascii_case(self.level.as_str()))
185        {
186            return Err(Error::Config(ConfigError::InvalidLogLevel {
187                level: self.level.clone(),
188                valid_levels: LOG_LEVELS.iter().map(|s| (*s).to_string()).collect(),
189            }));
190        }
191
192        // 验证保留天数(1-365天)
193        if self.retention_days == 0 || self.retention_days > 365 {
194            return Err(Error::Config(ConfigError::InvalidValue {
195                field: "logging.retention_days".to_string(),
196                value: self.retention_days.to_string(),
197                reason: "Retention days must be between 1 and 365".to_string(),
198            }));
199        }
200
201        Ok(())
202    }
203}
204
205impl Default for LoggingConfig {
206    fn default() -> Self {
207        Self {
208            file: "logs/sqllog2db.log".to_string(),
209            level: "info".to_string(),
210            retention_days: 7,
211        }
212    }
213}
214
215#[derive(Debug, Deserialize, Clone)]
216pub struct ExporterConfig {
217    #[cfg(feature = "csv")]
218    pub csv: Option<CsvExporter>,
219    #[cfg(feature = "jsonl")]
220    pub jsonl: Option<JsonlExporter>,
221    #[cfg(feature = "sqlite")]
222    pub sqlite: Option<SqliteExporter>,
223}
224
225impl ExporterConfig {
226    /// 获取 CSV 导出器配置
227    #[cfg(feature = "csv")]
228    #[must_use]
229    pub fn csv(&self) -> Option<&CsvExporter> {
230        self.csv.as_ref()
231    }
232
233    #[cfg(feature = "jsonl")]
234    /// 获取 JSONL 导出器配置
235    #[must_use]
236    pub fn jsonl(&self) -> Option<&JsonlExporter> {
237        self.jsonl.as_ref()
238    }
239
240    #[cfg(feature = "sqlite")]
241    /// 获取 `SQLite` 导出器配置
242    #[must_use]
243    pub fn sqlite(&self) -> Option<&SqliteExporter> {
244        self.sqlite.as_ref()
245    }
246
247    /// 检查是否有任何导出器配置
248    #[must_use]
249    pub fn has_exporters(&self) -> bool {
250        let mut found = false;
251        #[cfg(feature = "csv")]
252        {
253            found = found || self.csv.is_some();
254        }
255        #[cfg(feature = "jsonl")]
256        {
257            found = found || self.jsonl.is_some();
258        }
259        #[cfg(feature = "sqlite")]
260        {
261            found = found || self.sqlite.is_some();
262        }
263        found
264    }
265
266    /// 统计配置的导出器总数
267    #[must_use]
268    pub fn total_exporters(&self) -> usize {
269        let mut count = 0;
270        #[cfg(feature = "csv")]
271        {
272            if self.csv.is_some() {
273                count += 1;
274            }
275        }
276        #[cfg(feature = "jsonl")]
277        {
278            if self.jsonl.is_some() {
279                count += 1;
280            }
281        }
282        #[cfg(feature = "sqlite")]
283        {
284            if self.sqlite.is_some() {
285                count += 1;
286            }
287        }
288        count
289    }
290
291    /// 验证导出器配置(只支持单个导出器)
292    pub fn validate(&self) -> Result<()> {
293        if !self.has_exporters() {
294            return Err(Error::Config(ConfigError::NoExporters));
295        }
296
297        let total = self.total_exporters();
298        if total > 1 {
299            eprintln!("Warning: {total} exporters configured, but only one is supported.");
300            eprintln!("Will use the first exporter by priority: CSV > JSONL > SQLite");
301        }
302
303        Ok(())
304    }
305}
306
307impl Default for ExporterConfig {
308    fn default() -> Self {
309        Self {
310            #[cfg(feature = "csv")]
311            csv: Some(CsvExporter::default()),
312            #[cfg(feature = "jsonl")]
313            jsonl: None,
314            #[cfg(feature = "sqlite")]
315            sqlite: None,
316        }
317    }
318}
319
320#[cfg(feature = "jsonl")]
321#[derive(Debug, Deserialize, Clone)]
322pub struct JsonlExporter {
323    /// JSONL 输出文件路径
324    pub file: String,
325    /// 是否覆盖已存在的文件
326    #[serde(default = "default_true")]
327    pub overwrite: bool,
328    /// 是否追加模式
329    #[serde(default)]
330    pub append: bool,
331}
332
333#[cfg(feature = "jsonl")]
334impl Default for JsonlExporter {
335    fn default() -> Self {
336        Self {
337            file: "export/sqllog2db.jsonl".to_string(),
338            overwrite: true,
339            append: false,
340        }
341    }
342}
343
344#[cfg(feature = "sqlite")]
345#[derive(Debug, Deserialize, Clone)]
346pub struct SqliteExporter {
347    /// `SQLite` 数据库文件路径
348    pub database_url: String,
349    /// 表名
350    #[serde(default = "default_table_name")]
351    pub table_name: String,
352    /// 是否覆盖已存在的表
353    #[serde(default = "default_true")]
354    pub overwrite: bool,
355    /// 是否追加模式
356    #[serde(default)]
357    pub append: bool,
358}
359
360#[cfg(feature = "sqlite")]
361impl Default for SqliteExporter {
362    fn default() -> Self {
363        Self {
364            database_url: "export/sqllog2db.db".to_string(),
365            table_name: "sqllog_records".to_string(),
366            overwrite: true,
367            append: false,
368        }
369    }
370}
371
372#[cfg(feature = "csv")]
373#[derive(Debug, Deserialize, Clone)]
374pub struct CsvExporter {
375    /// CSV 输出文件路径
376    pub file: String,
377    /// 是否覆盖已存在的文件
378    #[serde(default = "default_true")]
379    pub overwrite: bool,
380    /// 是否追加模式(暂未实现)
381    #[serde(default)]
382    pub append: bool,
383}
384
385#[cfg(feature = "csv")]
386impl Default for CsvExporter {
387    fn default() -> Self {
388        Self {
389            file: "outputs/sqllog.csv".to_string(),
390            overwrite: true,
391            append: false,
392        }
393    }
394}