otelite_storage/
config.rs1use otelite_core::storage::{Result, StorageError};
4use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct StorageConfig {
10 pub data_dir: PathBuf,
12
13 pub retention_days: u32,
15
16 pub purge_schedule: String,
18
19 pub auto_purge_enabled: bool,
21
22 pub purge_batch_size: usize,
24}
25
26impl Default for StorageConfig {
27 fn default() -> Self {
28 Self {
29 data_dir: Self::default_data_dir(),
30 retention_days: 90,
31 purge_schedule: "0 2 * * *".to_string(), auto_purge_enabled: true,
33 purge_batch_size: 1000,
34 }
35 }
36}
37
38impl StorageConfig {
39 pub fn default_data_dir() -> PathBuf {
40 dirs::home_dir()
41 .unwrap_or_else(|| PathBuf::from("."))
42 .join(".otelite")
43 .join("data")
44 }
45
46 pub fn from_env() -> Result<Self> {
48 let mut config = Self::default();
49
50 if let Ok(data_dir) = std::env::var("OTELITE_DATA_DIR") {
51 config.data_dir = PathBuf::from(data_dir);
52 }
53
54 if let Ok(retention_days) = std::env::var("OTELITE_RETENTION_DAYS") {
55 config.retention_days = retention_days
56 .parse()
57 .map_err(|e| StorageError::ConfigError(format!("Invalid retention_days: {}", e)))?;
58 }
59
60 if let Ok(purge_schedule) = std::env::var("OTELITE_PURGE_SCHEDULE") {
61 config.purge_schedule = purge_schedule;
62 }
63
64 if let Ok(auto_purge) = std::env::var("OTELITE_AUTO_PURGE_ENABLED") {
65 config.auto_purge_enabled = auto_purge.parse().unwrap_or(true);
66 }
67
68 config.validate()?;
69 Ok(config)
70 }
71
72 pub fn validate(&self) -> Result<()> {
74 if self.retention_days < 1 || self.retention_days > 365 {
75 return Err(StorageError::ConfigError(
76 "Retention days must be between 1 and 365".to_string(),
77 ));
78 }
79
80 if self.purge_batch_size == 0 {
81 return Err(StorageError::ConfigError(
82 "Purge batch size must be greater than 0".to_string(),
83 ));
84 }
85
86 Ok(())
87 }
88
89 pub fn with_data_dir(mut self, data_dir: PathBuf) -> Self {
91 self.data_dir = data_dir;
92 self
93 }
94
95 pub fn with_retention_days(mut self, days: u32) -> Self {
97 self.retention_days = days;
98 self
99 }
100
101 pub fn with_purge_schedule(mut self, schedule: String) -> Self {
103 self.purge_schedule = schedule;
104 self
105 }
106
107 pub fn with_auto_purge(mut self, enabled: bool) -> Self {
109 self.auto_purge_enabled = enabled;
110 self
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_default_config() {
120 let config = StorageConfig::default();
121 assert_eq!(config.retention_days, 90);
122 assert_eq!(config.purge_schedule, "0 2 * * *");
123 assert!(config.auto_purge_enabled);
124 assert_eq!(config.purge_batch_size, 1000);
125 }
126
127 #[test]
128 fn test_default_data_dir_is_dotfile() {
129 let dir = StorageConfig::default_data_dir();
131 let home = dirs::home_dir().expect("home dir must exist in test env");
132 assert!(
133 dir.starts_with(&home),
134 "data dir {:?} should be under home {:?}",
135 dir,
136 home
137 );
138 let rel = dir.strip_prefix(&home).unwrap();
139 assert_eq!(
140 rel,
141 std::path::Path::new(".otelite/data"),
142 "data dir must be ~/.otelite/data, got {:?}",
143 dir
144 );
145 }
146
147 #[test]
148 fn test_config_validation() {
149 let mut config = StorageConfig::default();
150 assert!(config.validate().is_ok());
151
152 config.retention_days = 0;
153 assert!(config.validate().is_err());
154
155 config.retention_days = 366;
156 assert!(config.validate().is_err());
157
158 config.retention_days = 90;
159 config.purge_batch_size = 0;
160 assert!(config.validate().is_err());
161 }
162
163 #[test]
164 fn test_config_builder() {
165 let config = StorageConfig::default()
166 .with_retention_days(30)
167 .with_auto_purge(false);
168
169 assert_eq!(config.retention_days, 30);
170 assert!(!config.auto_purge_enabled);
171 }
172}