Skip to main content

rivven_core/
config.rs

1use crate::storage::TieredStorageConfig;
2use serde::{Deserialize, Serialize};
3
4/// Configuration for Rivven
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Config {
7    /// Server bind address
8    pub bind_address: String,
9
10    /// Server port
11    pub port: u16,
12
13    /// Default number of partitions for new topics
14    pub default_partitions: u32,
15
16    /// Enable disk persistence
17    pub enable_persistence: bool,
18
19    /// Data directory for persistence
20    pub data_dir: String,
21
22    /// Maximum segment size in bytes
23    pub max_segment_size: u64,
24
25    /// Log level
26    pub log_level: String,
27
28    /// Tiered storage configuration (hot/warm/cold data tiering)
29    #[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, // 1GB
42            log_level: "info".to_string(),
43            tiered_storage: TieredStorageConfig::default(),
44        }
45    }
46}
47
48impl Config {
49    /// Create a new configuration
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    /// Set the bind address
55    pub fn with_bind_address(mut self, address: String) -> Self {
56        self.bind_address = address;
57        self
58    }
59
60    /// Set the port
61    pub fn with_port(mut self, port: u16) -> Self {
62        self.port = port;
63        self
64    }
65
66    /// Set the default partitions
67    pub fn with_default_partitions(mut self, partitions: u32) -> Self {
68        self.default_partitions = partitions;
69        self
70    }
71
72    /// Enable or disable persistence
73    pub fn with_persistence(mut self, enabled: bool) -> Self {
74        self.enable_persistence = enabled;
75        self
76    }
77
78    /// Set the data directory
79    pub fn with_data_dir(mut self, data_dir: String) -> Self {
80        self.data_dir = data_dir;
81        self
82    }
83
84    /// Set tiered storage configuration
85    pub fn with_tiered_storage(mut self, config: TieredStorageConfig) -> Self {
86        self.tiered_storage = config;
87        self
88    }
89
90    /// Enable tiered storage with default settings
91    pub fn with_tiered_storage_enabled(mut self) -> Self {
92        self.tiered_storage.enabled = true;
93        self
94    }
95
96    /// Get the server address
97    pub fn server_address(&self) -> String {
98        format!("{}:{}", self.bind_address, self.port)
99    }
100
101    /// Validate the configuration, returning an error message if invalid
102    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}