diff --git a/src-tauri/src/watchers/delta.rs b/src-tauri/src/watchers/delta.rs index 8addb6734..b7c3fd00a 100644 --- a/src-tauri/src/watchers/delta.rs +++ b/src-tauri/src/watchers/delta.rs @@ -136,7 +136,7 @@ impl DeltaWatchers { Ok(()) } - pub fn unwatch(&mut self, project: projects::Project) -> Result<()> { + pub fn unwatch(&mut self, project: &projects::Project) -> Result<()> { if let Some(mut watcher) = self.watchers.remove(&project.path) { watcher.unwatch(Path::new(&project.path))?; } diff --git a/src-tauri/src/watchers/git.rs b/src-tauri/src/watchers/git.rs new file mode 100644 index 000000000..d59cbd58f --- /dev/null +++ b/src-tauri/src/watchers/git.rs @@ -0,0 +1,108 @@ +use crate::projects; +use anyhow::Result; +use notify::{Config, RecommendedWatcher, Watcher}; +use std::{ + collections::HashMap, + path::Path, + sync::{mpsc, Arc, Mutex}, +}; + +pub enum Event { + Head, +} + +pub struct GitWatchers { + watchers: HashMap, +} + +impl GitWatchers { + pub fn new() -> Self { + Self { + watchers: HashMap::new(), + } + } + + pub fn unwatch(&mut self, project: &projects::Project) -> Result<()> { + if let Some(mut watcher) = self.watchers.remove(&project.id) { + watcher.unwatch(&Path::new(&Path::new(&project.path).join(".git")))?; + } + Ok(()) + } + + pub fn watch(&mut self, project: projects::Project) -> Result> { + let (tx, rx) = mpsc::channel(); + let mut watcher = RecommendedWatcher::new(tx, Config::default())?; + + watcher.watch( + Path::new(&Path::new(&project.path).join(".git")), + notify::RecursiveMode::Recursive, + )?; + self.watchers.insert(project.id.clone(), watcher); + + let project = Arc::new(Mutex::new(project.clone())); + + let (events_sender, events_receiver) = mpsc::channel(); + tauri::async_runtime::spawn_blocking(move || { + log::info!("{}: watching git", project.lock().unwrap().id); + + let project = project.lock().unwrap().clone(); + let project_path = Path::new(&project.path); + + while let Ok(event) = rx.recv() { + if let Err(e) = event { + log::error!("{}: notify event error: {:#}", project.id.clone(), e); + continue; + } + + let event = event.unwrap(); + + for file_path in event.paths { + let relative_file_path = file_path + .strip_prefix(Path::new(&project_path.join(".git"))) + .unwrap(); + + match is_interesting_event(&event.kind) { + Some(kind_string) => { + log::info!( + "{}: \"{}\" {}", + project.id, + relative_file_path.display(), + kind_string + ); + + if relative_file_path == Path::new("HEAD") { + if let Err(e) = events_sender.send(Event::Head) { + log::error!("{}: failed to send event: {:#}", project.id, e); + } + } + } + None => { + // ignore + } + } + } + } + log::info!("{}: stopped watching git", project.id); + }); + + Ok(events_receiver) + } +} + +fn is_interesting_event(kind: ¬ify::EventKind) -> Option { + match kind { + notify::EventKind::Create(notify::event::CreateKind::File) => { + Some("file created".to_string()) + } + notify::EventKind::Modify(notify::event::ModifyKind::Data(_)) => { + Some("file modified".to_string()) + } + notify::EventKind::Modify(notify::event::ModifyKind::Name(_)) => { + Some("file renamed".to_string()) + } + notify::EventKind::Remove(notify::event::RemoveKind::File) => { + Some("file removed".to_string()) + } + _ => None, + } +} diff --git a/src-tauri/src/watchers/mod.rs b/src-tauri/src/watchers/mod.rs index 42bfca2d4..d47e14634 100644 --- a/src-tauri/src/watchers/mod.rs +++ b/src-tauri/src/watchers/mod.rs @@ -1,4 +1,5 @@ mod delta; +mod git; mod session; #[cfg(test)] @@ -16,6 +17,7 @@ use std::{ pub struct Watcher { session_watcher: session::SessionWatcher, delta_watcher: delta::DeltaWatchers, + git_watcher: git::GitWatchers, } impl Watcher { @@ -27,9 +29,11 @@ impl Watcher { let session_watcher = session::SessionWatcher::new(projects_storage, users_storage, deltas_searcher); let delta_watcher = delta::DeltaWatchers::new(); + let git_watcher = git::GitWatchers::new(); Self { session_watcher, delta_watcher, + git_watcher, } } @@ -53,12 +57,14 @@ impl Watcher { .watch(sender.clone(), project.clone(), lock_file.clone())?; self.session_watcher .watch(sender.clone(), project.clone(), lock_file.clone())?; + self.git_watcher.watch(project.clone())?; Ok(()) } pub fn unwatch(&mut self, project: projects::Project) -> Result<()> { - self.delta_watcher.unwatch(project)?; + self.delta_watcher.unwatch(&project)?; + self.git_watcher.unwatch(&project)?; // TODO: how to unwatch session ? Ok(()) }