use anyhow::{Context, Result};
use std::path::Path;
use std::time::Duration;
use notify::RecursiveMode;
use notify_debouncer_full::DebounceEventResult;
use tokio::sync::mpsc::UnboundedSender;
use super::backend::{KizuDebouncer, format_notify_errors, new_kizu_debouncer};
use super::matcher::SharedMatcher;
use super::{WatchEvent, WatchRoot, WatchSource};
const HEAD_DEBOUNCE: Duration = Duration::from_millis(100);
pub(in crate::watcher) fn git_state_watch_roots(
git_dir: &Path,
common_git_dir: &Path,
) -> Vec<WatchRoot> {
vec![
WatchRoot {
path: git_dir.join("HEAD"),
recursive_mode: RecursiveMode::NonRecursive,
compare_contents: true,
source: WatchSource::GitPerWorktreeHead,
},
WatchRoot {
path: common_git_dir.join("refs"),
recursive_mode: RecursiveMode::Recursive,
compare_contents: true,
source: WatchSource::GitRefs,
},
WatchRoot {
path: common_git_dir.to_path_buf(),
recursive_mode: RecursiveMode::NonRecursive,
compare_contents: true,
source: WatchSource::GitCommonRoot,
},
]
}
pub(in crate::watcher) fn spawn_git_state_debouncer(
watch_root: &WatchRoot,
matcher: SharedMatcher,
tx: UnboundedSender<WatchEvent>,
) -> Result<KizuDebouncer> {
let source = watch_root.source;
let compare_contents = watch_root.compare_contents;
let mut debouncer = new_kizu_debouncer(
HEAD_DEBOUNCE,
compare_contents,
move |result: DebounceEventResult| {
let events = match result {
Ok(events) => events,
Err(errors) => {
let message = format_notify_errors(source, &errors);
let _ = tx.send(WatchEvent::Error { source, message });
return;
}
};
let Ok(guard) = matcher.read() else {
let _ = tx.send(WatchEvent::Error {
source,
message: format!(
"watcher [{}]: baseline matcher lock poisoned",
source.label()
),
});
return;
};
let baseline_touched = events
.iter()
.any(|ev| ev.event.paths.iter().any(|p| guard.matches(p)));
drop(guard);
if baseline_touched {
let _ = tx.send(WatchEvent::GitHead(source));
}
},
)
.context("failed to create git_dir debouncer")?;
debouncer
.watch(&watch_root.path, watch_root.recursive_mode)
.with_context(|| format!("failed to watch git_dir at {}", watch_root.path.display()))?;
Ok(debouncer)
}