Skip to main content

provenant/cache/
config.rs

1use 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}