use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::mpsc::{self, Receiver};
use std::thread;
use chrono::{DateTime, Local};
use crate::domain::error::Result;
use crate::domain::repo::GitInfo;
use crate::storage::cache;
use crate::storage::git_client::GitClient;
pub use crate::storage::cache::GitInfoCache;
pub enum StatusUpdate {
Started {
path: PathBuf,
},
Done {
path: PathBuf,
info: GitInfo,
},
}
pub fn load_cache(path: &Path) -> GitInfoCache {
cache::load(path)
}
pub fn save_cache(
path: &Path,
infos: &[(PathBuf, GitInfo)],
fetched_at: Option<DateTime<Local>>,
) -> Result<()> {
cache::save(path, infos, fetched_at)
}
pub fn collect_all(
client: &dyn GitClient,
paths: &[PathBuf],
fetch: bool,
) -> Vec<(PathBuf, GitInfo)> {
paths
.iter()
.map(|path| {
if fetch {
client.fetch(path);
}
(path.clone(), client.collect(path))
})
.collect()
}
pub fn spawn_refresh(
client: Arc<dyn GitClient>,
paths: Vec<PathBuf>,
fetch: bool,
) -> Receiver<StatusUpdate> {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
for path in paths {
if sender
.send(StatusUpdate::Started { path: path.clone() })
.is_err()
{
break;
}
if fetch {
client.fetch(&path);
}
let info = client.collect(&path);
if sender.send(StatusUpdate::Done { path, info }).is_err() {
break;
}
}
});
receiver
}
#[cfg(test)]
mod tests {
use std::path::Path;
use super::*;
use crate::domain::stats::GitStats;
struct FakeClient;
impl GitClient for FakeClient {
fn collect(&self, _path: &Path) -> GitInfo {
GitInfo {
valid: true,
current_branch_name: Some("main".to_string()),
changes: Some(0),
..GitInfo::default()
}
}
fn fetch(&self, _path: &Path) {}
fn log(&self, _path: &Path, _max: usize) -> Vec<String> {
Vec::new()
}
fn stats(&self, _path: &Path) -> GitStats {
GitStats::default()
}
}
#[test]
fn collect_all_returns_info_per_path() {
let paths = vec![
PathBuf::from("/a"),
PathBuf::from("/b"),
PathBuf::from("/c"),
];
let results = collect_all(&FakeClient, &paths, false);
assert_eq!(results.len(), 3);
assert!(results.iter().all(|(_, info)| info.valid));
}
#[test]
fn spawn_refresh_streams_every_path() {
let paths = vec![PathBuf::from("/a"), PathBuf::from("/b")];
let rx = spawn_refresh(Arc::new(FakeClient), paths, false);
let mut done = 0;
while let Ok(update) = rx.recv() {
if matches!(update, StatusUpdate::Done { .. }) {
done += 1;
}
}
assert_eq!(done, 2);
}
}