use super::global::default_true;
use crate::support::processing::template::OutputFormat;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileSinkConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_log_path")]
pub path: PathBuf,
#[serde(default = "default_max_size")]
pub max_size: String,
#[serde(default = "default_rotation_time")]
pub rotation_time: String,
#[serde(default = "default_keep_files")]
pub keep_files: u32,
#[serde(default = "default_true")]
pub compress: bool,
#[serde(default = "default_compression_level")]
pub compression_level: i32,
#[serde(default)]
pub encrypt: bool,
#[serde(default)]
pub encryption_key_env: Option<String>,
#[serde(default = "default_retention_days")]
pub retention_days: u32,
#[serde(default = "default_max_total_size")]
pub max_total_size: String,
#[serde(default = "default_cleanup_interval_minutes")]
pub cleanup_interval_minutes: u64,
#[serde(default = "default_batch_size")]
pub batch_size: usize,
#[serde(default = "default_flush_interval_ms")]
pub flush_interval_ms: u64,
#[serde(default = "default_true")]
pub masking_enabled: bool,
#[serde(default)]
pub output_format: OutputFormat,
}
fn default_log_path() -> PathBuf {
PathBuf::from("logs/app.log")
}
fn default_max_size() -> String {
"100MB".to_string()
}
fn default_rotation_time() -> String {
"daily".to_string()
}
fn default_keep_files() -> u32 {
30
}
fn default_compression_level() -> i32 {
3
}
fn default_retention_days() -> u32 {
30
}
fn default_max_total_size() -> String {
"1GB".to_string()
}
fn default_cleanup_interval_minutes() -> u64 {
60
}
fn default_batch_size() -> usize {
100
}
fn default_flush_interval_ms() -> u64 {
100
}
impl Default for FileSinkConfig {
fn default() -> Self {
Self {
enabled: default_true(),
path: default_log_path(),
max_size: default_max_size(),
rotation_time: default_rotation_time(),
keep_files: default_keep_files(),
compress: default_true(),
compression_level: default_compression_level(),
encrypt: false,
encryption_key_env: None,
retention_days: default_retention_days(),
max_total_size: default_max_total_size(),
cleanup_interval_minutes: default_cleanup_interval_minutes(),
batch_size: default_batch_size(),
flush_interval_ms: default_flush_interval_ms(),
masking_enabled: default_true(),
output_format: OutputFormat::default(),
}
}
}
impl FileSinkConfig {
pub fn validate(&self) -> Result<(), String> {
if self.compression_level < 1 || self.compression_level > 22 {
return Err(format!(
"compression_level must be between 1 and 22, got {}",
self.compression_level
));
}
if self.encrypt && self.encryption_key_env.is_none() {
return Err("encrypt is enabled but encryption_key_env is not set".to_string());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_rejects_invalid_compression_level() {
let mut config = FileSinkConfig::default();
config.compression_level = 0;
assert!(config.validate().is_err());
config.compression_level = 23;
assert!(config.validate().is_err());
config.compression_level = 3;
assert!(config.validate().is_ok());
}
#[test]
fn test_validate_rejects_encrypt_without_key_env() {
let mut config = FileSinkConfig::default();
config.encrypt = true;
config.encryption_key_env = None;
assert!(config.validate().is_err());
config.encryption_key_env = Some("MY_KEY".to_string());
assert!(config.validate().is_ok());
}
}