1use auths_core::ports::storage::StorageError;
2use git2::Repository;
3use std::path::{Path, PathBuf};
4use std::sync::Mutex;
5
6pub struct GitRepo {
19 inner: Mutex<Repository>,
20 path: PathBuf,
21}
22
23impl GitRepo {
24 pub fn open(path: impl AsRef<Path>) -> Result<Self, StorageError> {
34 let path = path.as_ref().to_path_buf();
35 let inner = Repository::open(&path).map_err(|e| StorageError::Io(e.to_string()))?;
36 Ok(Self {
37 inner: Mutex::new(inner),
38 path,
39 })
40 }
41
42 pub fn init(path: impl AsRef<Path>) -> Result<Self, StorageError> {
52 let path = path.as_ref().to_path_buf();
53 let inner = Repository::init(&path).map_err(|e| StorageError::Io(e.to_string()))?;
54 disable_gc(&inner)?;
55 Ok(Self {
56 inner: Mutex::new(inner),
57 path,
58 })
59 }
60
61 pub(crate) fn with_repo<T>(
62 &self,
63 f: impl FnOnce(&Repository) -> Result<T, StorageError>,
64 ) -> Result<T, StorageError> {
65 let repo = self
66 .inner
67 .lock()
68 .map_err(|e| StorageError::Io(format!("mutex poisoned: {}", e)))?;
69 f(&repo)
70 }
71
72 pub fn path(&self) -> &Path {
73 &self.path
74 }
75}
76
77fn disable_gc(repo: &Repository) -> Result<(), StorageError> {
87 let mut cfg = repo.config().map_err(|e| StorageError::Io(e.to_string()))?;
88 cfg.set_i32("gc.auto", 0)
89 .map_err(|e| StorageError::Io(e.to_string()))?;
90 cfg.set_str("gc.pruneExpire", "never")
91 .map_err(|e| StorageError::Io(e.to_string()))?;
92 Ok(())
93}
94
95#[cfg(test)]
96#[allow(clippy::unwrap_used)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn init_disables_gc() {
102 let dir = tempfile::tempdir().unwrap();
103 let repo = GitRepo::init(dir.path()).unwrap();
104 repo.with_repo(|r| {
105 let cfg = r.config().map_err(|e| StorageError::Io(e.to_string()))?;
106 assert_eq!(cfg.get_i32("gc.auto").unwrap(), 0);
107 assert_eq!(cfg.get_string("gc.pruneExpire").unwrap(), "never");
108 Ok(())
109 })
110 .unwrap();
111 }
112}