sqry_cli/persistence/
config.rs1use std::path::PathBuf;
7
8pub const DEFAULT_MAX_HISTORY_ENTRIES: usize = 10_000;
10
11pub const DEFAULT_MAX_INDEX_BYTES: u64 = 10 * 1024 * 1024;
13
14pub const ENV_CONFIG_DIR: &str = "SQRY_CONFIG_DIR";
16
17pub const ENV_NO_HISTORY: &str = "SQRY_NO_HISTORY";
19
20pub const ENV_NO_REDACT: &str = "SQRY_NO_REDACT";
22
23#[derive(Debug, Clone)]
25pub struct PersistenceConfig {
26 pub global_dir_override: Option<PathBuf>,
28
29 pub local_dir_override: Option<PathBuf>,
31
32 pub history_enabled: bool,
34
35 pub max_history_entries: usize,
37
38 pub max_index_bytes: u64,
40
41 pub redact_secrets: bool,
43}
44
45impl Default for PersistenceConfig {
46 fn default() -> Self {
47 Self {
48 global_dir_override: None,
49 local_dir_override: None,
50 history_enabled: true,
51 max_history_entries: DEFAULT_MAX_HISTORY_ENTRIES,
52 max_index_bytes: DEFAULT_MAX_INDEX_BYTES,
53 redact_secrets: true,
55 }
56 }
57}
58
59impl PersistenceConfig {
60 #[must_use]
64 pub fn from_env() -> Self {
65 Self::from_env_and_cli(None, None, false)
66 }
67
68 #[must_use]
79 pub fn from_env_and_cli(
80 cli_config_dir: Option<PathBuf>,
81 cli_max_history: Option<usize>,
82 cli_no_history: bool,
83 ) -> Self {
84 let global_dir_override =
86 cli_config_dir.or_else(|| std::env::var(ENV_CONFIG_DIR).ok().map(PathBuf::from));
87
88 let history_enabled = if cli_no_history {
90 false
91 } else {
92 std::env::var(ENV_NO_HISTORY)
93 .map(|v| !["1", "true", "yes"].contains(&v.to_lowercase().as_str()))
94 .unwrap_or(true)
95 };
96
97 let redact_secrets = std::env::var(ENV_NO_REDACT)
99 .map(|v| !["1", "true", "yes"].contains(&v.to_lowercase().as_str()))
100 .unwrap_or(true);
101
102 Self {
103 global_dir_override,
104 local_dir_override: None,
105 history_enabled,
106 max_history_entries: cli_max_history.unwrap_or(DEFAULT_MAX_HISTORY_ENTRIES),
107 max_index_bytes: DEFAULT_MAX_INDEX_BYTES,
108 redact_secrets,
109 }
110 }
111
112 pub fn global_config_dir(&self) -> anyhow::Result<PathBuf> {
121 if let Some(ref override_path) = self.global_dir_override {
122 return Ok(override_path.clone());
123 }
124
125 dirs::config_dir()
127 .map(|p| p.join("sqry"))
128 .ok_or_else(|| anyhow::anyhow!("Could not determine config directory"))
129 }
130
131 #[must_use]
140 pub fn local_config_dir(&self, project_root: &std::path::Path) -> PathBuf {
141 self.local_dir_override
142 .clone()
143 .unwrap_or_else(|| project_root.to_path_buf())
144 }
145}
146
147pub fn global_config_dir() -> anyhow::Result<PathBuf> {
155 dirs::config_dir()
156 .map(|p| p.join("sqry"))
157 .ok_or_else(|| anyhow::anyhow!("Could not determine config directory"))
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use serial_test::serial;
164
165 #[test]
166 fn test_default_config() {
167 let config = PersistenceConfig::default();
168
169 assert!(config.global_dir_override.is_none());
170 assert!(config.local_dir_override.is_none());
171 assert!(config.history_enabled);
172 assert_eq!(config.max_history_entries, DEFAULT_MAX_HISTORY_ENTRIES);
173 assert_eq!(config.max_index_bytes, DEFAULT_MAX_INDEX_BYTES);
174 assert!(
176 config.redact_secrets,
177 "redact_secrets should default to true"
178 );
179 }
180
181 #[test]
182 #[serial]
183 fn test_cli_config_dir_takes_precedence() {
184 unsafe {
186 std::env::set_var(ENV_CONFIG_DIR, "/env/path");
187 }
188
189 let config =
190 PersistenceConfig::from_env_and_cli(Some(PathBuf::from("/cli/path")), None, false);
191
192 assert_eq!(config.global_dir_override, Some(PathBuf::from("/cli/path")));
193
194 unsafe {
195 std::env::remove_var(ENV_CONFIG_DIR);
196 }
197 }
198
199 #[test]
200 #[serial]
201 fn test_env_config_dir_fallback() {
202 unsafe {
203 std::env::set_var(ENV_CONFIG_DIR, "/env/path");
204 }
205
206 let config = PersistenceConfig::from_env_and_cli(None, None, false);
207
208 assert_eq!(config.global_dir_override, Some(PathBuf::from("/env/path")));
209
210 unsafe {
211 std::env::remove_var(ENV_CONFIG_DIR);
212 }
213 }
214
215 #[test]
216 fn test_cli_no_history_disables_history() {
217 let config = PersistenceConfig::from_env_and_cli(None, None, true);
218
219 assert!(!config.history_enabled);
220 }
221
222 #[test]
223 #[serial]
224 fn test_env_no_history_disables_history() {
225 unsafe {
226 std::env::set_var(ENV_NO_HISTORY, "1");
227 }
228
229 let config = PersistenceConfig::from_env_and_cli(None, None, false);
230
231 assert!(!config.history_enabled);
232
233 unsafe {
234 std::env::remove_var(ENV_NO_HISTORY);
235 }
236 }
237
238 #[test]
239 #[serial]
240 fn test_redact_secrets_default_enabled() {
241 let config = PersistenceConfig::from_env_and_cli(None, None, false);
243 assert!(
244 config.redact_secrets,
245 "redact_secrets should default to true"
246 );
247 }
248
249 #[test]
250 #[serial]
251 fn test_redact_secrets_disabled_via_env() {
252 unsafe {
253 std::env::set_var(ENV_NO_REDACT, "1");
254 }
255
256 let config = PersistenceConfig::from_env_and_cli(None, None, false);
257
258 assert!(
259 !config.redact_secrets,
260 "SQRY_NO_REDACT=1 should disable redaction"
261 );
262
263 unsafe {
264 std::env::remove_var(ENV_NO_REDACT);
265 }
266 }
267
268 #[test]
269 fn test_global_config_dir_with_override() {
270 let config = PersistenceConfig {
271 global_dir_override: Some(PathBuf::from("/custom/path")),
272 ..Default::default()
273 };
274
275 let dir = config.global_config_dir().expect("should succeed");
276 assert_eq!(dir, PathBuf::from("/custom/path"));
277 }
278
279 #[test]
280 fn test_local_config_dir() {
281 let config = PersistenceConfig::default();
282 let project = PathBuf::from("/home/user/project");
283
284 let local_dir = config.local_config_dir(&project);
285 assert_eq!(local_dir, project);
286 }
287
288 #[test]
289 fn test_local_config_dir_with_override() {
290 let config = PersistenceConfig {
291 local_dir_override: Some(PathBuf::from("/custom/local")),
292 ..Default::default()
293 };
294 let project = PathBuf::from("/home/user/project");
295
296 let local_dir = config.local_config_dir(&project);
297 assert_eq!(local_dir, PathBuf::from("/custom/local"));
298 }
299}