gitai/remote/cache/
manager.rs1use std::collections::{HashMap, HashSet};
2use std::sync::{Arc, Mutex};
3
4use super::key_generator::CacheKeyGenerator;
5use crate::remote::models::{
6 cached_repo::CachedRepository, repo_config::RepositoryConfiguration,
7 wire_operation::WireOperation,
8};
9
10type CacheKey = String;
11
12#[derive(Default)]
13pub struct CacheManager {
14 cache: Arc<Mutex<HashMap<CacheKey, CachedRepository>>>,
16}
17
18impl CacheManager {
19 pub fn new() -> Self {
20 Self {
21 cache: Arc::new(Mutex::new(HashMap::new())),
22 }
23 }
24
25 pub fn get_or_schedule_fetch(
28 &self,
29 config: &RepositoryConfiguration,
30 ) -> Result<String, String> {
31 let key = CacheKeyGenerator::generate_key(config);
32 let mut cache = self
33 .cache
34 .lock()
35 .expect("Failed to lock cache mutex, likely due to a poisoned mutex (another thread panicked while holding the lock)");
36
37 if let Some(cached_repo) = cache.get(&key) {
38 Ok(cached_repo.local_cache_path.clone())
40 } else {
41 let cache_path = Self::get_cache_path(&key)?;
44
45 let new_cached_repo = CachedRepository::new(
46 config.url.clone(),
47 config.branch.clone(),
48 cache_path.clone(),
49 "placeholder_commit_hash".to_string(), );
51
52 cache.insert(key, new_cached_repo);
53
54 Ok(cache_path)
56 }
57 }
58
59 fn get_cache_path(key: &str) -> Result<String, String> {
61 let cache_dir = std::env::temp_dir().join("git-wire-cache").join(key);
62
63 std::fs::create_dir_all(&cache_dir)
64 .map_err(|e| format!("Failed to create cache directory: {e}"))?;
65
66 Ok(cache_dir.to_string_lossy().to_string())
67 }
68
69 pub fn plan_fetch_operations(
73 &self,
74 configs: &[RepositoryConfiguration],
75 ) -> Result<(Vec<RepositoryConfiguration>, Vec<WireOperation>), String> {
76 let mut unique_configs: Vec<RepositoryConfiguration> = Vec::new();
78 let mut seen_keys: HashSet<String> = HashSet::new();
79 let mut operations: Vec<WireOperation> = Vec::new();
80
81 for config in configs {
82 let key = CacheKeyGenerator::generate_key(config);
83
84 if !seen_keys.contains(&key) {
85 unique_configs.push(config.clone());
87 seen_keys.insert(key);
88 }
89
90 let cached_path = self.get_or_schedule_fetch(config)?;
92
93 operations.push(WireOperation::new(config.clone(), cached_path));
95 }
96
97 Ok((unique_configs, operations))
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_cache_manager_creation() {
107 let cache_manager = CacheManager::new();
108 assert_eq!(
109 cache_manager
110 .cache
111 .lock()
112 .expect("Failed to lock cache in test")
113 .len(),
114 0
115 );
116 }
117
118 #[test]
119 fn test_get_or_schedule_fetch_new_repo() {
120 let cache_manager = CacheManager::new();
121 let config = RepositoryConfiguration::new(
122 "https://github.com/example/repo.git".to_string(),
123 "main".to_string(),
124 "./src/module1".to_string(),
125 vec!["src/".to_string()],
126 None,
127 None,
128 );
129
130 let result = cache_manager.get_or_schedule_fetch(&config);
131 assert!(result.is_ok());
132
133 let key = CacheKeyGenerator::generate_key(&config);
135 let cache = cache_manager
136 .cache
137 .lock()
138 .expect("Failed to lock cache in test");
139 assert!(cache.contains_key(&key));
140 }
141
142 #[test]
143 fn test_plan_fetch_operations_with_duplicates() {
144 let cache_manager = CacheManager::new();
145
146 let configs = vec![
148 RepositoryConfiguration::new(
149 "https://github.com/example/repo.git".to_string(),
150 "main".to_string(),
151 "./src/module1".to_string(),
152 vec!["src/".to_string(), "lib/".to_string()],
153 None,
154 None,
155 ),
156 RepositoryConfiguration::new(
157 "https://github.com/example/repo.git".to_string(), "main".to_string(),
159 "./src/module2".to_string(),
160 vec!["utils/".to_string()],
161 None,
162 None,
163 ),
164 RepositoryConfiguration::new(
165 "https://github.com/other/repo.git".to_string(), "main".to_string(),
167 "./src/module3".to_string(),
168 vec!["docs/".to_string()],
169 None,
170 None,
171 ),
172 ];
173
174 let (unique_configs, operations) = cache_manager
175 .plan_fetch_operations(&configs)
176 .expect("Failed to plan fetch operations");
177
178 assert_eq!(unique_configs.len(), 2);
180
181 assert_eq!(operations.len(), 3);
183 }
184}