use color_eyre::Result;
use crossterm::event::{Event, EventStream, KeyCode, KeyEventKind, KeyModifiers};
use futures::{FutureExt, StreamExt};
use std::path::PathBuf;
use tokio::sync::mpsc;
pub enum GitDataUpdate {
RemoteStatus(usize, String),
Status(usize, String),
FetchProgress(usize),
FetchComplete(usize),
CloneProgress(usize),
CloneComplete(usize),
DeleteProgress(usize),
DeleteComplete(usize), }
pub enum TerminalEvent {
Key(KeyCode, KeyModifiers),
GitUpdate(GitDataUpdate),
}
pub struct EventHandler {
terminal_events: EventStream,
git_rx: mpsc::UnboundedReceiver<GitDataUpdate>,
git_tx: mpsc::UnboundedSender<GitDataUpdate>,
}
impl EventHandler {
pub fn new<F>(repo_count: usize, get_path: F, fetch_repos: bool, update_local: bool) -> Self
where
F: Fn(usize) -> PathBuf + Send + 'static,
{
let (tx, git_rx) = mpsc::unbounded_channel();
for idx in 0..repo_count {
let path = get_path(idx);
let tx_clone = tx.clone();
let should_fetch = fetch_repos;
let should_update = update_local;
tokio::spawn(async move {
let remote_status = tokio::task::spawn_blocking({
let path = path.clone();
move || crate::git_repo::GitRepo::read_remote_status(&path)
})
.await
.unwrap_or_else(|_| "error".to_string());
let status = tokio::task::spawn_blocking({
let path = path.clone();
move || crate::git_repo::GitRepo::read_status(&path)
})
.await
.unwrap_or_else(|_| "error".to_string());
let _ = tx_clone.send(GitDataUpdate::RemoteStatus(idx, remote_status.clone()));
let _ = tx_clone.send(GitDataUpdate::Status(idx, status));
if should_fetch && remote_status != "local-only" && remote_status != "error" {
let _ = tx_clone.send(GitDataUpdate::FetchProgress(idx));
let fetch_result = tokio::task::spawn_blocking({
let path = path.clone();
move || crate::git_repo::GitRepo::fetch(&path, should_update)
})
.await;
if fetch_result.is_ok() {
let new_remote_status = tokio::task::spawn_blocking(move || {
crate::git_repo::GitRepo::read_remote_status(&path)
})
.await
.unwrap_or_else(|_| "error".to_string());
let _ = tx_clone.send(GitDataUpdate::RemoteStatus(idx, new_remote_status));
}
let _ = tx_clone.send(GitDataUpdate::FetchComplete(idx));
}
});
}
let tx_clone = tx.clone();
Self {
terminal_events: EventStream::new(),
git_rx,
git_tx: tx_clone,
}
}
pub fn git_tx(&self) -> mpsc::UnboundedSender<GitDataUpdate> {
self.git_tx.clone()
}
pub async fn next(&mut self) -> Result<Option<TerminalEvent>> {
tokio::select! {
Some(update) = self.git_rx.recv() => {
Ok(Some(TerminalEvent::GitUpdate(update)))
}
Some(event) = self.terminal_events.next().fuse() => {
match event? {
Event::Key(key) if key.kind == KeyEventKind::Press => {
Ok(Some(TerminalEvent::Key(key.code, key.modifiers)))
}
_ => Ok(None)
}
}
else => Ok(None)
}
}
}