use std::path::Path; use std::sync::Arc; use crate::GitError; use git2::Repository; use models::repos::repo; #[derive(Clone)] pub struct GitDomain { pub(crate) repo: Arc, } // SAFETY: git2's Repository uses internal locking for thread-safe operations. // We additionally enforce exclusive access via Arc::get_mut in repo_mut(). // All mutable access is gated through the synchronous methods of HookMetaDataSync, // which are called from a single blocking thread per sync cycle. #[allow(unsafe_code)] unsafe impl Send for GitDomain {} #[allow(unsafe_code)] unsafe impl Sync for GitDomain {} impl GitDomain { pub fn from_model(model: repo::Model) -> crate::GitResult { let repo = Repository::open(model.storage_path).map_err(|e| GitError::Internal(e.to_string()))?; Ok(Self { repo: Arc::new(repo), }) } pub fn open>(path: P) -> crate::GitResult { let repo = Repository::open(path).map_err(|e| GitError::Internal(e.to_string()))?; Ok(Self { repo: Arc::new(repo), }) } pub fn open_workdir>(path: P) -> crate::GitResult { let repo = Repository::open_bare(path).map_err(|e| GitError::Internal(e.to_string()))?; Ok(Self { repo: Arc::new(repo), }) } pub fn init_bare>(path: P) -> crate::GitResult { let repo = Repository::init_bare(path).map_err(|e| GitError::Internal(e.to_string()))?; Ok(Self { repo: Arc::new(repo), }) } pub fn repo(&self) -> &Repository { &self.repo } pub fn repo_mut(&mut self) -> crate::GitResult<&mut Repository> { Arc::get_mut(&mut self.repo) .ok_or_else(|| GitError::Internal("GitDomain requires exclusive access".to_string())) } }