1use crate::{Error, Result, SPath};
2use notify::{self, RecommendedWatcher, RecursiveMode};
3use notify_debouncer_full::{DebounceEventHandler, DebounceEventResult, Debouncer, RecommendedCache, new_debouncer};
4use std::path::Path;
5use std::time::Duration;
7
8use flume::{Receiver, Sender};
10pub use notify_debouncer_full::DebouncedEvent;
11use std::collections::HashSet;
12
13const WATCH_DEBOUNCE_MS: u64 = 200;
14
15#[derive(Debug)]
20pub struct SEvent {
21 pub spath: SPath,
22 pub skind: SEventKind,
23}
24
25#[derive(Debug, Clone, Eq, Hash, PartialEq)]
27pub enum SEventKind {
28 Create,
29 Modify,
30 Remove,
31 Other,
32}
33
34impl From<notify::EventKind> for SEventKind {
35 fn from(val: notify::EventKind) -> Self {
36 match val {
37 notify::EventKind::Any => SEventKind::Other,
38 notify::EventKind::Access(_) => SEventKind::Other,
39 notify::EventKind::Create(_) => SEventKind::Create,
40 notify::EventKind::Modify(_) => SEventKind::Modify,
41 notify::EventKind::Remove(_) => SEventKind::Remove,
42 notify::EventKind::Other => SEventKind::Other,
43 }
44 }
45}
46
47#[allow(unused)]
49pub struct SWatcher {
50 pub rx: Receiver<Vec<SEvent>>,
51 notify_full_debouncer: Debouncer<RecommendedWatcher, RecommendedCache>,
53}
54
55pub fn watch(path: impl AsRef<Path>) -> Result<SWatcher> {
62 let (tx, rx) = flume::unbounded();
63
64 let path = path.as_ref();
65 let handler = EventHandler { tx };
66 let mut debouncer =
67 new_debouncer(Duration::from_millis(WATCH_DEBOUNCE_MS), None, handler).map_err(|err| Error::FailToWatch {
68 path: path.to_string_lossy().to_string(),
69 cause: err.to_string(),
70 })?;
71
72 if !path.exists() {
73 return Err(Error::CantWatchPathNotFound(path.to_string_lossy().to_string()));
74 }
75
76 debouncer
77 .watch(path, RecursiveMode::Recursive)
78 .map_err(|err| Error::FailToWatch {
79 path: path.to_string_lossy().to_string(),
80 cause: err.to_string(),
81 })?;
82
83 let swatcher = SWatcher {
84 rx,
85 notify_full_debouncer: debouncer,
86 };
87
88 Ok(swatcher)
89}
90
91struct EventHandler {
93 tx: Sender<Vec<SEvent>>,
94}
95
96impl DebounceEventHandler for EventHandler {
97 fn handle_event(&mut self, result: DebounceEventResult) {
98 match result {
99 Ok(events) => {
100 let sevents = build_sevents(events);
101 if !sevents.is_empty() {
102 let _ = self.tx.send(sevents);
103 }
104 }
105 Err(err) => println!("simple-fs - handle_event error {err:?}"), }
107 }
108}
109
110#[derive(Hash, Eq, PartialEq)]
111struct SEventKey {
112 spath_string: String,
113 skind: SEventKind,
114}
115
116fn build_sevents(events: Vec<DebouncedEvent>) -> Vec<SEvent> {
117 let mut sevents_set: HashSet<SEventKey> = HashSet::new();
118
119 let mut sevents = Vec::new();
120
121 for devent in events {
122 let event = devent.event;
123 let skind = SEventKind::from(event.kind);
124
125 for path in event.paths {
126 if let Some(spath) = SPath::from_std_path_buf_ok(path) {
127 let key = SEventKey {
128 spath_string: spath.to_string(),
129 skind: skind.clone(),
130 };
131
132 if !sevents_set.contains(&key) {
134 sevents.push(SEvent {
135 spath,
136 skind: skind.clone(),
137 });
138
139 sevents_set.insert(key);
140 }
141 }
142 }
143 }
144
145 sevents
146}