use std::{ fs, path::{Path, PathBuf}, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, task::Waker, time::Duration, }; use globset::GlobSet; use notify::RecursiveMode; use notify_debouncer_full::{new_debouncer_opt, DebounceEventResult}; pub type Watcher = notify_debouncer_full::Debouncer< notify::RecommendedWatcher, notify_debouncer_full::RecommendedCache, >; pub struct WatcherState { pub config_path: Option, pub left_obj_path: Option, pub right_obj_path: Option, pub patterns: GlobSet, } pub fn create_watcher( modified: Arc, project_dir: &Path, patterns: GlobSet, waker: Waker, ) -> notify::Result { let base_dir = fs::canonicalize(project_dir)?; let base_dir_clone = base_dir.clone(); let timeout = Duration::from_millis(200); let config = notify::Config::default().with_poll_interval(Duration::from_secs(2)); let mut debouncer = new_debouncer_opt( timeout, None, move |result: DebounceEventResult| match result { Ok(events) => { let mut any_match = false; for event in events.iter() { if !matches!( event.kind, notify::EventKind::Modify(..) | notify::EventKind::Create(..) | notify::EventKind::Remove(..) ) { continue; } for path in &event.paths { let Ok(path) = path.strip_prefix(&base_dir_clone) else { continue; }; if patterns.is_match(path) { // log::info!("File modified: {}", path.display()); any_match = true; } } } if any_match { modified.store(true, Ordering::Relaxed); waker.wake_by_ref(); } } Err(errors) => errors.iter().for_each(|e| log::error!("Watch error: {e:?}")), }, notify_debouncer_full::RecommendedCache::new(), config, )?; debouncer.watch(base_dir, RecursiveMode::Recursive)?; Ok(debouncer) }