use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct EventsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
#[validate(nested)]
pub batching: BatchingConfig,
#[serde(default = "default_channel_capacity")]
#[validate(range(min = 16, max = 65536))]
pub channel_capacity: usize,
#[serde(default = "default_subject")]
pub subject: String,
#[serde(default)]
pub policy: EventPolicyConfig,
}
impl Default for EventsConfig {
fn default() -> Self {
Self {
enabled: false,
batching: BatchingConfig::default(),
channel_capacity: default_channel_capacity(),
subject: default_subject(),
policy: EventPolicyConfig::default(),
}
}
}
fn default_channel_capacity() -> usize {
1024
}
fn default_subject() -> String {
"kvbm.events".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct BatchingConfig {
#[serde(default = "default_window_duration_ms")]
#[validate(range(min = 1, max = 10000))]
pub window_duration_ms: u64,
#[serde(default = "default_max_batch_size")]
#[validate(range(min = 1, max = 65536))]
pub max_batch_size: usize,
}
impl Default for BatchingConfig {
fn default() -> Self {
Self {
window_duration_ms: default_window_duration_ms(),
max_batch_size: default_max_batch_size(),
}
}
}
fn default_window_duration_ms() -> u64 {
10
}
fn default_max_batch_size() -> usize {
1024
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum EventPolicyConfig {
#[default]
PowerOfTwo,
All,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = EventsConfig::default();
assert!(!config.enabled);
assert_eq!(config.batching.window_duration_ms, 10);
assert_eq!(config.batching.max_batch_size, 1024);
assert_eq!(config.channel_capacity, 1024);
assert_eq!(config.subject, "kvbm.events");
assert_eq!(config.policy, EventPolicyConfig::PowerOfTwo);
}
#[test]
fn test_serde_roundtrip() {
let json = r#"{
"enabled": true,
"batching": {
"window_duration_ms": 50,
"max_batch_size": 512
},
"channel_capacity": 2048,
"subject": "my.events",
"policy": "all"
}"#;
let config: EventsConfig = serde_json::from_str(json).unwrap();
assert!(config.enabled);
assert_eq!(config.batching.window_duration_ms, 50);
assert_eq!(config.batching.max_batch_size, 512);
assert_eq!(config.channel_capacity, 2048);
assert_eq!(config.subject, "my.events");
assert_eq!(config.policy, EventPolicyConfig::All);
let serialized = serde_json::to_string(&config).unwrap();
let deserialized: EventsConfig = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.enabled, config.enabled);
assert_eq!(deserialized.policy, config.policy);
}
#[test]
fn test_empty_json_uses_defaults() {
let json = r#"{}"#;
let config: EventsConfig = serde_json::from_str(json).unwrap();
assert!(!config.enabled);
assert_eq!(config.batching.window_duration_ms, 10);
}
#[test]
fn test_partial_config() {
let json = r#"{"enabled": true}"#;
let config: EventsConfig = serde_json::from_str(json).unwrap();
assert!(config.enabled);
assert_eq!(config.batching.window_duration_ms, 10);
assert_eq!(config.channel_capacity, 1024);
}
#[test]
fn test_validation() {
let config = EventsConfig {
enabled: true,
batching: BatchingConfig {
window_duration_ms: 10,
max_batch_size: 1024,
},
channel_capacity: 1024,
subject: "test".to_string(),
policy: EventPolicyConfig::PowerOfTwo,
};
assert!(config.validate().is_ok());
}
}