gitai/remote/cache/
lock.rs

1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3
4// Type alias for repository URL
5type RepoUrl = String;
6
7#[derive(Default)]
8pub struct RepositoryLockManager {
9    // Tracks locks for each repository
10    locks: Arc<Mutex<HashMap<RepoUrl, Arc<Mutex<bool>>>>>,
11}
12
13impl RepositoryLockManager {
14    pub fn new() -> Self {
15        Self {
16            locks: Arc::new(Mutex::new(HashMap::new())),
17        }
18    }
19
20    /// Acquire a lock for a specific repository, blocking until available
21    pub fn acquire_lock(&self, repo_url: &str) -> Result<(), String> {
22        let mut locks = self
23            .locks
24            .lock()
25            .expect("Failed to acquire global lock for repository locks");
26
27        // Check if we already have a lock for this URL
28        let repo_lock = locks
29            .entry(repo_url.to_string())
30            .or_insert_with(|| Arc::new(Mutex::new(false)));
31
32        // Clone the Arc to use for locking
33        let lock_clone = Arc::clone(repo_lock);
34        drop(locks); // Release the global lock
35
36        // Acquire the specific repository lock
37        let guard = lock_clone
38            .lock()
39            .unwrap_or_else(|_| panic!("Failed to acquire repository lock for URL: {repo_url}"));
40
41        // Hold the lock for the duration of the function, then release it
42        // In a real implementation, you'd want to return a guard that manages the lock lifetime
43        // For now, just simulate the lock being held briefly
44        std::mem::drop(guard);
45
46        Ok(())
47    }
48
49    /// Try to acquire a lock for a specific repository without blocking
50    pub fn try_acquire_lock(&self, repo_url: &str) -> Result<bool, String> {
51        let mut locks = self
52            .locks
53            .lock()
54            .expect("Failed to acquire global lock for repository locks");
55
56        // Check if we already have a lock for this URL
57        let repo_lock = locks
58            .entry(repo_url.to_string())
59            .or_insert_with(|| Arc::new(Mutex::new(false)));
60
61        // Clone the Arc to use for locking
62        let lock_clone = Arc::clone(repo_lock);
63        drop(locks); // Release the global lock
64
65        // Try to acquire the specific repository lock
66        match lock_clone.try_lock() {
67            Ok(guard) => {
68                // Successfully acquired the lock
69                std::mem::drop(guard); // Release immediately for this simplified version
70                Ok(true)
71            }
72            Err(_) => Ok(false), // Lock is already held by another thread
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_lock_manager_creation() {
83        let lock_manager = RepositoryLockManager::new();
84        assert_eq!(
85            lock_manager
86                .locks
87                .lock()
88                .expect("Failed to acquire lock on repository locks map")
89                .len(),
90            0
91        );
92    }
93
94    #[test]
95    fn test_acquire_lock() {
96        let lock_manager = RepositoryLockManager::new();
97        let repo_url = "https://github.com/example/repo.git";
98
99        let result = lock_manager.acquire_lock(repo_url);
100        assert!(result.is_ok());
101    }
102
103    #[test]
104    fn test_try_acquire_lock() {
105        let lock_manager = RepositoryLockManager::new();
106        let repo_url = "https://github.com/example/repo.git";
107
108        // Initially should be able to acquire
109        let result = lock_manager.try_acquire_lock(repo_url);
110        assert!(result.is_ok());
111        assert!(
112            result.expect("Failed to unwrap try_acquire_lock result"),
113            "Expected to acquire lock successfully on first attempt"
114        );
115    }
116}