git_repository.rs

  1use git2::Repository;
  2use parking_lot::Mutex;
  3use std::{path::Path, sync::Arc};
  4use util::ResultExt;
  5
  6pub trait GitRepository: Send + Sync {
  7    fn boxed_clone(&self) -> Box<dyn GitRepository>;
  8    fn is_path_managed_by(&self, path: &Path) -> bool;
  9    fn is_path_in_git_folder(&self, path: &Path) -> bool;
 10    fn content_path(&self) -> &Path;
 11    fn git_dir_path(&self) -> &Path;
 12    fn last_scan_id(&self) -> usize;
 13    fn set_scan_id(&mut self, scan_id: usize);
 14    fn with_repo(&mut self, f: Box<dyn FnOnce(&mut git2::Repository)>);
 15}
 16
 17#[derive(Clone)]
 18pub struct RealGitRepository {
 19    // Path to folder containing the .git file or directory
 20    content_path: Arc<Path>,
 21    // Path to the actual .git folder.
 22    // Note: if .git is a file, this points to the folder indicated by the .git file
 23    git_dir_path: Arc<Path>,
 24    last_scan_id: usize,
 25    libgit_repository: Arc<Mutex<git2::Repository>>,
 26}
 27
 28impl RealGitRepository {
 29    pub fn open(
 30        abs_dotgit_path: &Path,
 31        content_path: &Arc<Path>,
 32    ) -> Option<Box<dyn GitRepository>> {
 33        Repository::open(&abs_dotgit_path)
 34            .log_err()
 35            .map::<Box<dyn GitRepository>, _>(|libgit_repository| {
 36                Box::new(Self {
 37                    content_path: content_path.clone(),
 38                    git_dir_path: libgit_repository.path().into(),
 39                    last_scan_id: 0,
 40                    libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)),
 41                })
 42            })
 43    }
 44}
 45
 46impl GitRepository for RealGitRepository {
 47    fn boxed_clone(&self) -> Box<dyn GitRepository> {
 48        Box::new(self.clone())
 49    }
 50
 51    fn is_path_managed_by(&self, path: &Path) -> bool {
 52        path.starts_with(&self.content_path)
 53    }
 54
 55    fn is_path_in_git_folder(&self, path: &Path) -> bool {
 56        path.starts_with(&self.git_dir_path)
 57    }
 58
 59    fn content_path(&self) -> &Path {
 60        self.content_path.as_ref()
 61    }
 62
 63    fn git_dir_path(&self) -> &Path {
 64        self.git_dir_path.as_ref()
 65    }
 66
 67    fn last_scan_id(&self) -> usize {
 68        self.last_scan_id
 69    }
 70
 71    fn set_scan_id(&mut self, scan_id: usize) {
 72        self.last_scan_id = scan_id;
 73    }
 74
 75    fn with_repo(&mut self, f: Box<dyn FnOnce(&mut git2::Repository)>) {
 76        let mut git2 = self.libgit_repository.lock();
 77        f(&mut git2)
 78    }
 79}
 80
 81impl PartialEq for &Box<dyn GitRepository> {
 82    fn eq(&self, other: &Self) -> bool {
 83        self.content_path() == other.content_path()
 84    }
 85}
 86impl Eq for &Box<dyn GitRepository> {}
 87
 88#[cfg(any(test, feature = "test-support"))]
 89#[derive(Clone)]
 90pub struct FakeGitRepository {
 91    // Path to folder containing the .git file or directory
 92    content_path: Arc<Path>,
 93    // Path to the actual .git folder.
 94    // Note: if .git is a file, this points to the folder indicated by the .git file
 95    git_dir_path: Arc<Path>,
 96    last_scan_id: usize,
 97}
 98
 99impl FakeGitRepository {
100    pub fn new(abs_dotgit_path: &Path, content_path: &Arc<Path>) -> FakeGitRepository {
101        Self {
102            content_path: content_path.clone(),
103            git_dir_path: abs_dotgit_path.into(),
104            last_scan_id: 0,
105        }
106    }
107}
108
109#[cfg(any(test, feature = "test-support"))]
110impl GitRepository for FakeGitRepository {
111    fn boxed_clone(&self) -> Box<dyn GitRepository> {
112        Box::new(self.clone())
113    }
114
115    fn is_path_managed_by(&self, path: &Path) -> bool {
116        path.starts_with(&self.content_path)
117    }
118
119    fn is_path_in_git_folder(&self, path: &Path) -> bool {
120        path.starts_with(&self.git_dir_path)
121    }
122
123    fn content_path(&self) -> &Path {
124        self.content_path.as_ref()
125    }
126
127    fn git_dir_path(&self) -> &Path {
128        self.git_dir_path.as_ref()
129    }
130
131    fn last_scan_id(&self) -> usize {
132        self.last_scan_id
133    }
134
135    fn set_scan_id(&mut self, scan_id: usize) {
136        self.last_scan_id = scan_id;
137    }
138
139    fn with_repo(&mut self, _: Box<dyn FnOnce(&mut git2::Repository)>) {
140        unimplemented!();
141    }
142}