Skip to main content

oo_ide/editor/
file_watcher.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::sync::mpsc::{channel, Receiver};
4use std::time::Duration;
5
6use notify::{RecommendedWatcher, RecursiveMode};
7use notify_debouncer_mini::{new_debouncer, DebouncedEventKind, Debouncer};
8
9use crate::prelude::*;
10
11#[derive(Debug)]
12pub struct FileWatcher {
13    watcher: Debouncer<RecommendedWatcher>,
14    events_rx: Receiver<Result<Vec<notify_debouncer_mini::DebouncedEvent>, notify::Error>>,
15    watched_paths: HashMap<PathBuf, PathBuf>,
16}
17
18impl FileWatcher {
19    pub fn new() -> Self {
20        let (tx, rx) = channel();
21        let watcher = new_debouncer(Duration::from_millis(500), tx).unwrap();
22        Self {
23            watcher,
24            events_rx: rx,
25            watched_paths: HashMap::new(),
26        }
27    }
28
29    pub fn watch(&mut self, path: PathBuf) -> Result<()> {
30        if self.watched_paths.contains_key(&path) {
31            return Ok(());
32        }
33
34        let dir = path.parent().unwrap_or(&path);
35        if !dir.exists() {
36            return Ok(());
37        }
38
39        self.watcher
40            .watcher()
41            .watch(dir, RecursiveMode::NonRecursive)
42            .map_err(|e| anyhow!("Failed to watch {:?}: {}", dir, e))?;
43
44        self.watched_paths.insert(path.clone(), dir.to_path_buf());
45
46        Ok(())
47    }
48
49    pub fn unwatch(&mut self, path: &PathBuf) -> Result<()> {
50        if let Some(dir) = self.watched_paths.remove(path) {
51            self.watcher
52                .watcher()
53                .unwatch(&dir)
54                .map_err(|e| anyhow!("Failed to unwatch {:?}: {}", dir, e))?;
55        }
56        Ok(())
57    }
58
59    pub fn check_events(&self) -> Vec<PathBuf> {
60        let mut changed = Vec::new();
61        while let Ok(events) = self.events_rx.try_recv() {
62            if let Ok(events) = events {
63                for event in events {
64                    if matches!(event.kind, DebouncedEventKind::Any) {
65                        changed.push(event.path);
66                    }
67                }
68            }
69        }
70        changed
71    }
72
73    pub fn is_watching(&self, path: &PathBuf) -> bool {
74        self.watched_paths.contains_key(path)
75    }
76}
77
78impl Default for FileWatcher {
79    fn default() -> Self {
80        Self::new()
81    }
82}