use crate::error::{ LogResult};
use std::collections::HashMap;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct LogConfig {
pub level: String,
pub file_path: Option<String>,
pub console_output: bool,
pub verbose: bool,
pub format: LogFormat,
pub rotation: Option<RotationConfig>,
pub highlight: HighlightConfig,
pub filters: Vec<FilterConfig>,
pub custom_fields: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct LogFormat {
pub time_format: String,
pub show_file: bool,
pub show_line: bool,
pub show_module: bool,
pub template: Option<String>,
}
#[derive(Debug, Clone)]
pub struct RotationConfig {
pub max_size: u64,
pub max_files: u32,
pub compress: bool,
}
#[derive(Debug, Clone)]
pub struct HighlightConfig {
pub enabled: bool,
pub keywords: Vec<String>,
pub color_map: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct FilterConfig {
pub filter_type: FilterType,
pub rule: String,
pub enabled: bool,
}
#[derive(Debug, Clone)]
pub enum FilterType {
Regex,
Keyword,
Level,
Module,
}
impl Default for LogConfig {
fn default() -> Self {
Self {
level: "info".to_string(),
file_path: None,
console_output: true,
verbose: false,
format: LogFormat::default(),
rotation: None,
highlight: HighlightConfig::default(),
filters: Vec::new(),
custom_fields: HashMap::new(),
}
}
}
impl Default for LogFormat {
fn default() -> Self {
Self {
time_format: "%Y-%m-%d %H:%M:%S".to_string(),
show_file: true,
show_line: true,
show_module: false,
template: None,
}
}
}
impl Default for HighlightConfig {
fn default() -> Self {
Self {
enabled: false,
keywords: Vec::new(),
color_map: HashMap::new(),
}
}
}
pub struct ConfigBuilder {
config: LogConfig,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self {
config: LogConfig::default(),
}
}
pub fn level<S: Into<String>>(mut self, level: S) -> Self {
self.config.level = level.into();
self
}
pub fn file<P: AsRef<Path>>(mut self, path: P) -> Self {
self.config.file_path = Some(path.as_ref().to_string_lossy().to_string());
self
}
pub fn console(mut self, enabled: bool) -> Self {
self.config.console_output = enabled;
self
}
pub fn verbose(mut self, enabled: bool) -> Self {
self.config.verbose = enabled;
self
}
pub fn time_format<S: Into<String>>(mut self, format: S) -> Self {
self.config.format.time_format = format.into();
self
}
pub fn rotation(mut self, max_size: u64, max_files: u32, compress: bool) -> Self {
self.config.rotation = Some(RotationConfig {
max_size,
max_files,
compress,
});
self
}
pub fn highlight_keywords(mut self, keywords: Vec<String>) -> Self {
self.config.highlight.enabled = !keywords.is_empty();
self.config.highlight.keywords = keywords;
self
}
pub fn add_filter(mut self, filter_type: FilterType, rule: String) -> Self {
self.config.filters.push(FilterConfig {
filter_type,
rule,
enabled: true,
});
self
}
pub fn custom_field<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
self.config.custom_fields.insert(key.into(), value.into());
self
}
pub fn build(self) -> LogConfig {
self.config
}
}
pub struct ConfigLoader;
impl ConfigLoader {
pub fn from_file<P: AsRef<Path>>(_path: P) -> LogResult<LogConfig> {
Ok(LogConfig::default())
}
pub fn from_env() -> LogResult<LogConfig> {
Ok(LogConfig::default())
}
pub fn save_to_file<P: AsRef<Path>>(_config: &LogConfig, _path: P) -> LogResult<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_builder() {
let config = ConfigBuilder::new()
.level("debug")
.file("/tmp/test.log")
.console(true)
.verbose(true)
.highlight_keywords(vec!["ERROR".to_string(), "WARN".to_string()])
.build();
assert_eq!(config.level, "debug");
assert_eq!(config.file_path, Some("/tmp/test.log".to_string()));
assert!(config.console_output);
assert!(config.verbose);
assert!(config.highlight.enabled);
assert_eq!(config.highlight.keywords.len(), 2);
}
}