use std::collections::HashMap;
use std::path::Path;
use std::sync::{Arc, RwLock};
use crate::cache::{trim_utf8, Cache, ARGS_HEAD, ARGS_TOPLEVEL};
use crate::error::Error;
use crate::runner::run_git_sync;
struct PoolEntry {
cache: Arc<Cache>,
head_sha: String,
}
#[derive(Default)]
pub struct Pool {
entries: RwLock<HashMap<String, PoolEntry>>,
}
impl Pool {
pub fn new() -> Pool {
Pool::default()
}
pub fn get(&self, root: impl AsRef<Path>) -> Result<Option<Arc<Cache>>, Error> {
let root = root.as_ref();
let canonical = match run_git_sync(root, ARGS_TOPLEVEL) {
Ok(out) => trim_utf8(&out),
Err(_) => return Ok(None),
};
if canonical.is_empty() {
return Ok(None);
}
if let Some((cache, head_sha)) = self.snapshot(&canonical) {
if let Ok(out) = run_git_sync(Path::new(&canonical), ARGS_HEAD) {
if trim_utf8(&out) == head_sha {
return Ok(Some(cache));
}
}
}
let cache = match Cache::new(root)? {
Some(c) => Arc::new(c),
None => return Ok(None), };
let head_sha = cache.head_sha().to_string();
self.store(canonical, Arc::clone(&cache), head_sha);
Ok(Some(cache))
}
pub(crate) fn snapshot(&self, canonical: &str) -> Option<(Arc<Cache>, String)> {
let entries = self.entries.read().expect("gitmeta pool lock poisoned");
entries
.get(canonical)
.map(|e| (Arc::clone(&e.cache), e.head_sha.clone()))
}
pub(crate) fn store(&self, canonical: String, cache: Arc<Cache>, head_sha: String) {
let mut entries = self.entries.write().expect("gitmeta pool lock poisoned");
entries.insert(canonical, PoolEntry { cache, head_sha });
}
pub fn warm(&self, root: impl AsRef<Path>) -> Result<(), Error> {
self.get(root).map(|_| ())
}
pub fn len(&self) -> usize {
self.entries
.read()
.expect("gitmeta pool lock poisoned")
.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}