1use crate::storage::TieredStorageConfig;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Config {
7 pub bind_address: String,
9
10 pub port: u16,
12
13 pub default_partitions: u32,
15
16 pub enable_persistence: bool,
18
19 pub data_dir: String,
21
22 pub max_segment_size: u64,
24
25 pub log_level: String,
27
28 #[serde(default)]
30 pub tiered_storage: TieredStorageConfig,
31}
32
33impl Default for Config {
34 fn default() -> Self {
35 Self {
36 bind_address: "127.0.0.1".to_string(),
37 port: 9092,
38 default_partitions: 3,
39 enable_persistence: false,
40 data_dir: "./data".to_string(),
41 max_segment_size: 1024 * 1024 * 1024, log_level: "info".to_string(),
43 tiered_storage: TieredStorageConfig::default(),
44 }
45 }
46}
47
48impl Config {
49 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn with_bind_address(mut self, address: String) -> Self {
56 self.bind_address = address;
57 self
58 }
59
60 pub fn with_port(mut self, port: u16) -> Self {
62 self.port = port;
63 self
64 }
65
66 pub fn with_default_partitions(mut self, partitions: u32) -> Self {
68 self.default_partitions = partitions;
69 self
70 }
71
72 pub fn with_persistence(mut self, enabled: bool) -> Self {
74 self.enable_persistence = enabled;
75 self
76 }
77
78 pub fn with_data_dir(mut self, data_dir: String) -> Self {
80 self.data_dir = data_dir;
81 self
82 }
83
84 pub fn with_tiered_storage(mut self, config: TieredStorageConfig) -> Self {
86 self.tiered_storage = config;
87 self
88 }
89
90 pub fn with_tiered_storage_enabled(mut self) -> Self {
92 self.tiered_storage.enabled = true;
93 self
94 }
95
96 pub fn server_address(&self) -> String {
98 format!("{}:{}", self.bind_address, self.port)
99 }
100
101 pub fn validate(&self) -> std::result::Result<(), String> {
103 if self.default_partitions == 0 {
104 return Err("default_partitions must be > 0".into());
105 }
106 if self.max_segment_size == 0 {
107 return Err("max_segment_size must be > 0".into());
108 }
109 if self.max_segment_size < 1024 {
110 return Err("max_segment_size must be >= 1024 bytes".into());
111 }
112 if self.bind_address.is_empty() {
113 return Err("bind_address must not be empty".into());
114 }
115 if self.enable_persistence && self.data_dir.is_empty() {
116 return Err("data_dir must not be empty when persistence is enabled".into());
117 }
118 if !matches!(
119 self.log_level.as_str(),
120 "trace" | "debug" | "info" | "warn" | "error"
121 ) {
122 return Err(format!(
123 "invalid log_level '{}', expected one of: trace, debug, info, warn, error",
124 self.log_level
125 ));
126 }
127 Ok(())
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_default_config() {
137 let config = Config::default();
138 assert_eq!(config.bind_address, "127.0.0.1");
139 assert_eq!(config.port, 9092);
140 assert_eq!(config.default_partitions, 3);
141 assert!(!config.enable_persistence);
142 assert_eq!(config.data_dir, "./data");
143 assert_eq!(config.max_segment_size, 1024 * 1024 * 1024);
144 assert_eq!(config.log_level, "info");
145 }
146
147 #[test]
148 fn test_builder_pattern() {
149 let config = Config::new()
150 .with_bind_address("0.0.0.0".to_string())
151 .with_port(9093)
152 .with_default_partitions(6)
153 .with_persistence(true)
154 .with_data_dir("/var/lib/rivven".to_string());
155
156 assert_eq!(config.bind_address, "0.0.0.0");
157 assert_eq!(config.port, 9093);
158 assert_eq!(config.default_partitions, 6);
159 assert!(config.enable_persistence);
160 assert_eq!(config.data_dir, "/var/lib/rivven");
161 }
162
163 #[test]
164 fn test_server_address() {
165 let config = Config::default();
166 assert_eq!(config.server_address(), "127.0.0.1:9092");
167
168 let custom = Config::new()
169 .with_bind_address("10.0.0.1".to_string())
170 .with_port(9999);
171 assert_eq!(custom.server_address(), "10.0.0.1:9999");
172 }
173
174 #[test]
175 fn test_serialization() {
176 let config = Config::default();
177 let json = serde_json::to_string(&config).unwrap();
178 let deserialized: Config = serde_json::from_str(&json).unwrap();
179
180 assert_eq!(config.bind_address, deserialized.bind_address);
181 assert_eq!(config.port, deserialized.port);
182 assert_eq!(config.default_partitions, deserialized.default_partitions);
183 }
184}