use super::*;
#[derive(Default)]
pub struct PanelGit {
pub dir: PathBuf,
pub status: Option<crate::git::Status>,
running: Option<(PathBuf, Receiver<Option<crate::git::Status>>)>,
queued: bool,
}
impl AppState {
pub fn git_status(&self, is_left: bool) -> Option<&crate::git::Status> {
if is_left { self.git_left.status.as_ref() } else { self.git_right.status.as_ref() }
}
fn panel_git(&mut self, is_left: bool) -> &mut PanelGit {
if is_left { &mut self.git_left } else { &mut self.git_right }
}
pub(super) fn request_git_marks(&mut self, is_left: bool, dir: &Path, in_repository: bool) {
let wanted = in_repository && self.options.git_marks;
let git = self.panel_git(is_left);
if git.dir != dir {
git.status = None;
git.dir = dir.to_path_buf();
}
if !wanted {
git.status = None;
git.running = None;
git.queued = false;
return;
}
if git.running.is_some() {
git.queued = true;
} else {
git.start();
}
}
pub fn poll_git_marks(&mut self) {
for is_left in [true, false] {
let git = self.panel_git(is_left);
let Some((asked, answer)) = &git.running else {
continue;
};
let status = match answer.try_recv() {
Ok(status) => status,
Err(TryRecvError::Empty) => continue,
Err(TryRecvError::Disconnected) => None,
};
if *asked == git.dir {
git.status = status;
}
git.running = None;
if std::mem::take(&mut git.queued) {
git.start();
}
}
}
}
impl PanelGit {
fn start(&mut self) {
let (sender, answer) = mpsc::channel();
let dir = self.dir.clone();
std::thread::spawn(move || {
let _ = sender.send(crate::git::status(&dir).ok());
});
self.running = Some((self.dir.clone(), answer));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn one_git_at_a_time_and_one_more_for_what_came_meanwhile() {
let dir = std::env::temp_dir().join(format!("fm84-git-throttle-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let made = std::process::Command::new("git").arg("-C").arg(&dir).args(["init", "--quiet"]).output().is_ok_and(|output| output.status.success());
if !made {
return;
}
let mut app_state = AppState::new();
let other = dir.join("other");
for _ in 0..3 {
app_state.request_git_marks(true, &dir, true);
}
assert!(app_state.git_left.running.is_some() && app_state.git_left.queued);
let wait = |app_state: &mut AppState| {
let start = Instant::now();
while app_state.git_left.running.is_some() && start.elapsed() < Duration::from_secs(10) {
app_state.poll_git_marks();
std::thread::sleep(Duration::from_millis(5));
}
};
let start = Instant::now();
while app_state.git_left.queued && start.elapsed() < Duration::from_secs(10) {
app_state.poll_git_marks();
std::thread::sleep(Duration::from_millis(5));
}
assert!(app_state.git_left.running.is_some(), "the one queued was not started");
wait(&mut app_state);
assert!(app_state.git_left.status.is_some() && !app_state.git_left.queued);
app_state.request_git_marks(true, &dir, true);
std::fs::create_dir_all(&other).unwrap();
app_state.request_git_marks(true, &other, true);
assert!(app_state.git_left.status.is_none());
let start = Instant::now();
while app_state.git_left.queued && start.elapsed() < Duration::from_secs(10) {
app_state.poll_git_marks();
assert!(app_state.git_left.status.is_none(), "marks for the directory left behind");
std::thread::sleep(Duration::from_millis(5));
}
wait(&mut app_state);
assert!(app_state.git_left.status.is_some() && app_state.git_left.dir == other);
std::fs::remove_dir_all(&dir).unwrap();
}
}