oo_ide/editor/
file_watcher.rs1use 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}