use std::path::{Path, PathBuf};
use std::sync::mpsc::Sender;
use std::time::Duration;
use notify::{RecursiveMode, Watcher};
use crate::event::Msg;
fn interesting(path: &Path) -> bool {
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
name == "HEAD" || name == "index" || path.components().any(|c| c.as_os_str() == "refs")
}
pub fn spawn(git_dir: PathBuf, tx: Sender<Msg>) {
std::thread::spawn(move || {
let (wtx, wrx) = std::sync::mpsc::channel();
let Ok(mut watcher) =
notify::recommended_watcher(move |res: notify::Result<notify::Event>| {
if let Ok(ev) = res
&& ev.paths.iter().any(|p| interesting(p))
{
let _ = wtx.send(());
}
})
else {
return;
};
if watcher.watch(&git_dir, RecursiveMode::Recursive).is_err() {
return;
}
while wrx.recv().is_ok() {
while wrx.recv_timeout(Duration::from_millis(200)).is_ok() {}
if tx.send(Msg::Refresh).is_err() {
break;
}
}
});
}
const MAX_PATHS: usize = 300;
pub fn spawn_worktree(root: PathBuf, tx: Sender<Msg>) {
std::thread::spawn(move || {
let (wtx, wrx) = std::sync::mpsc::channel::<Option<PathBuf>>();
let Ok(mut watcher) =
notify::recommended_watcher(move |res: notify::Result<notify::Event>| {
match res {
Ok(ev) => {
if matches!(ev.kind, notify::EventKind::Other) {
let _ = wtx.send(None);
return;
}
for p in ev.paths {
let _ = wtx.send(Some(p));
}
}
Err(_) => {
let _ = wtx.send(None);
}
}
})
else {
return;
};
if watcher.watch(&root, RecursiveMode::Recursive).is_err() {
return;
}
while let Ok(first) = wrx.recv() {
let mut paths = Vec::new();
let mut unknown = first.is_none();
if let Some(p) = first {
paths.push(p);
}
while let Ok(next) = wrx.recv_timeout(Duration::from_millis(150)) {
match next {
Some(p) => paths.push(p),
None => unknown = true,
}
}
let names = (!unknown).then(|| relative_names(&root, paths)).flatten();
if tx.send(Msg::Dirty(names)).is_err() {
return;
}
}
});
}
fn relative_names(root: &Path, paths: Vec<PathBuf>) -> Option<Vec<String>> {
let real_root = root.canonicalize().ok();
let mut out: Vec<String> = Vec::new();
for p in paths {
if p.components().any(|c| c.as_os_str() == ".git") {
continue;
}
let rel = p
.strip_prefix(root)
.ok()
.or_else(|| real_root.as_deref().and_then(|r| p.strip_prefix(r).ok()))?;
let name = rel.to_str()?;
if name.is_empty() {
continue;
}
out.push(name.to_string());
if out.len() > MAX_PATHS {
return None;
}
}
(!out.is_empty()).then_some(out)
}