provenant/cache/
config.rs1use std::fs;
2use std::io;
3use std::path::{Path, PathBuf};
4
5pub const DEFAULT_CACHE_DIR_NAME: &str = ".provenant-cache";
6pub const CACHE_DIR_ENV_VAR: &str = "PROVENANT_CACHE";
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct CacheConfig {
10 root_dir: PathBuf,
11}
12
13impl CacheConfig {
14 pub fn new(root_dir: PathBuf) -> Self {
15 Self { root_dir }
16 }
17
18 pub fn from_scan_root(scan_root: &Path) -> Self {
19 Self::new(scan_root.join(DEFAULT_CACHE_DIR_NAME))
20 }
21
22 pub fn resolve_root_dir(
23 scan_root: &Path,
24 cli_cache_dir: Option<&Path>,
25 env_cache_dir: Option<&Path>,
26 ) -> PathBuf {
27 if let Some(path) = cli_cache_dir {
28 return path.to_path_buf();
29 }
30
31 if let Some(path) = env_cache_dir {
32 return path.to_path_buf();
33 }
34
35 scan_root.join(DEFAULT_CACHE_DIR_NAME)
36 }
37
38 pub fn from_overrides(
39 scan_root: &Path,
40 cli_cache_dir: Option<&Path>,
41 env_cache_dir: Option<&Path>,
42 ) -> Self {
43 if cli_cache_dir.is_none() && env_cache_dir.is_none() {
44 return Self::from_scan_root(scan_root);
45 }
46
47 Self::new(Self::resolve_root_dir(
48 scan_root,
49 cli_cache_dir,
50 env_cache_dir,
51 ))
52 }
53
54 pub fn root_dir(&self) -> &Path {
55 &self.root_dir
56 }
57
58 pub fn index_dir(&self) -> PathBuf {
59 self.root_dir.join("index")
60 }
61
62 pub fn scan_results_dir(&self) -> PathBuf {
63 self.root_dir.join("scan-results")
64 }
65
66 pub fn ensure_dirs(&self) -> io::Result<()> {
67 fs::create_dir_all(self.index_dir())?;
68 fs::create_dir_all(self.scan_results_dir())?;
69 Ok(())
70 }
71
72 pub fn clear(&self) -> io::Result<()> {
73 if self.root_dir().exists() {
74 fs::remove_dir_all(&self.root_dir)?;
75 }
76 Ok(())
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use tempfile::TempDir;
83
84 use super::*;
85
86 #[test]
87 fn test_from_scan_root_uses_expected_directory_name() {
88 let temp_dir = TempDir::new().expect("Failed to create temp dir");
89 let config = CacheConfig::from_scan_root(temp_dir.path());
90 assert_eq!(
91 config.root_dir(),
92 temp_dir.path().join(DEFAULT_CACHE_DIR_NAME)
93 );
94 }
95
96 #[test]
97 fn test_ensure_dirs_creates_expected_tree() {
98 let temp_dir = TempDir::new().expect("Failed to create temp dir");
99 let config = CacheConfig::from_scan_root(temp_dir.path());
100
101 config
102 .ensure_dirs()
103 .expect("Failed to create cache directories");
104
105 assert!(config.root_dir().exists());
106 assert!(config.index_dir().exists());
107 assert!(config.scan_results_dir().exists());
108 }
109
110 #[test]
111 fn test_resolve_root_dir_prefers_cli_then_env_then_default() {
112 let scan_root = Path::new("/scan-root");
113 let cli_dir = Path::new("/cli-cache");
114 let env_dir = Path::new("/env-cache");
115
116 assert_eq!(
117 CacheConfig::resolve_root_dir(scan_root, Some(cli_dir), Some(env_dir)),
118 cli_dir
119 );
120 assert_eq!(
121 CacheConfig::resolve_root_dir(scan_root, None, Some(env_dir)),
122 env_dir
123 );
124 assert_eq!(
125 CacheConfig::resolve_root_dir(scan_root, None, None),
126 PathBuf::from(format!("/scan-root/{DEFAULT_CACHE_DIR_NAME}"))
127 );
128 }
129
130 #[test]
131 fn test_clear_removes_cache_root_directory() {
132 let temp_dir = TempDir::new().expect("Failed to create temp dir");
133 let config = CacheConfig::new(temp_dir.path().join("cache-root"));
134
135 config
136 .ensure_dirs()
137 .expect("Failed to create cache directories");
138 assert!(config.root_dir().exists());
139
140 config.clear().expect("Failed to clear cache directory");
141 assert!(!config.root_dir().exists());
142 }
143}