Skip to main content

objdiff_core/build/
watcher.rs

1use std::{
2    fs,
3    path::{Path, PathBuf},
4    sync::{
5        Arc,
6        atomic::{AtomicBool, Ordering},
7    },
8    task::Waker,
9    time::Duration,
10};
11
12use globset::GlobSet;
13use notify::RecursiveMode;
14use notify_debouncer_full::{DebounceEventResult, new_debouncer_opt};
15
16pub type Watcher = notify_debouncer_full::Debouncer<
17    notify::RecommendedWatcher,
18    notify_debouncer_full::RecommendedCache,
19>;
20
21pub struct WatcherState {
22    pub config_path: Option<PathBuf>,
23    pub left_obj_path: Option<PathBuf>,
24    pub right_obj_path: Option<PathBuf>,
25    pub patterns: GlobSet,
26}
27
28pub fn create_watcher(
29    modified: Arc<AtomicBool>,
30    project_dir: &Path,
31    patterns: GlobSet,
32    ignore_patterns: GlobSet,
33    waker: Waker,
34) -> notify::Result<Watcher> {
35    let base_dir = fs::canonicalize(project_dir)?;
36    let base_dir_clone = base_dir.clone();
37    let timeout = Duration::from_millis(200);
38    let config = notify::Config::default().with_poll_interval(Duration::from_secs(2));
39    let mut debouncer = new_debouncer_opt(
40        timeout,
41        None,
42        move |result: DebounceEventResult| match result {
43            Ok(events) => {
44                let mut any_match = false;
45                for event in events.iter() {
46                    if !matches!(
47                        event.kind,
48                        notify::EventKind::Modify(..)
49                            | notify::EventKind::Create(..)
50                            | notify::EventKind::Remove(..)
51                    ) {
52                        continue;
53                    }
54                    for path in &event.paths {
55                        let Ok(path) = path.strip_prefix(&base_dir_clone) else {
56                            continue;
57                        };
58                        if patterns.is_match(path) && !ignore_patterns.is_match(path) {
59                            log::debug!("File modified: {}", path.display());
60                            any_match = true;
61                        }
62                    }
63                }
64                if any_match {
65                    modified.store(true, Ordering::Relaxed);
66                    waker.wake_by_ref();
67                }
68            }
69            Err(errors) => errors.iter().for_each(|e| log::error!("Watch error: {e:?}")),
70        },
71        notify_debouncer_full::RecommendedCache::new(),
72        config,
73    )?;
74    debouncer.watch(base_dir, RecursiveMode::Recursive)?;
75    Ok(debouncer)
76}