1use anyhow::{bail, Result};
91use derive_builder::Builder;
92use enumflags2::{bitflags, BitFlags};
93use fxhash::FxHashSet;
94use notify::event::{
95 AccessKind, CreateKind, DataChange, MetadataKind, ModifyKind, RemoveKind, RenameMode,
96};
97use notify_debouncer_full::{DebounceEventHandler, DebounceEventResult};
98use poolshark::global::GPooled;
99use std::{
100 borrow::Borrow,
101 hash::Hash,
102 ops::Deref,
103 path::{self, Path, PathBuf},
104 sync::{Arc, LazyLock},
105 time::Duration,
106};
107use tokio::{sync::mpsc, task};
108use watch_task::MAX_NOTIFY_BATCH;
109
110mod watch_task;
111
112#[cfg(test)]
113mod test;
114
115pub const MIN_POLL_INTERVAL: Duration = Duration::from_millis(100);
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
118pub struct Id(u64);
119
120impl Id {
121 fn new() -> Self {
122 use std::sync::atomic::{AtomicU64, Ordering};
123 static NEXT: AtomicU64 = AtomicU64::new(0);
124 Self(NEXT.fetch_add(1, Ordering::Relaxed))
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
129pub struct ArcPath(Arc<PathBuf>);
130
131impl ArcPath {
132 pub fn get_mut(&mut self) -> Option<&mut PathBuf> {
133 Arc::get_mut(&mut self.0)
134 }
135
136 pub fn make_mut(&mut self) -> &mut PathBuf {
137 Arc::make_mut(&mut self.0)
138 }
139
140 pub fn root() -> Self {
141 static ROOT: LazyLock<ArcPath> =
142 LazyLock::new(|| ArcPath::from(path::MAIN_SEPARATOR_STR));
143 ROOT.clone()
144 }
145}
146
147impl AsRef<Path> for ArcPath {
148 fn as_ref(&self) -> &Path {
149 &*self.0
150 }
151}
152
153impl Deref for ArcPath {
154 type Target = PathBuf;
155
156 fn deref(&self) -> &Self::Target {
157 &*self.0
158 }
159}
160
161impl Borrow<Path> for ArcPath {
162 fn borrow(&self) -> &Path {
163 &*self.0
164 }
165}
166
167impl From<&Path> for ArcPath {
168 fn from(value: &Path) -> Self {
169 Self(Arc::new(value.into()))
170 }
171}
172
173impl From<&str> for ArcPath {
174 fn from(value: &str) -> Self {
175 Self(Arc::new(PathBuf::from(value)))
176 }
177}
178
179impl From<PathBuf> for ArcPath {
180 fn from(value: PathBuf) -> Self {
181 Self(Arc::new(value))
182 }
183}
184
185impl From<&PathBuf> for ArcPath {
186 fn from(value: &PathBuf) -> Self {
187 Self(Arc::new(value.clone()))
188 }
189}
190
191impl From<&ArcPath> for ArcPath {
192 fn from(value: &ArcPath) -> Self {
193 value.clone()
194 }
195}
196
197impl PartialEq<Path> for ArcPath {
198 fn eq(&self, other: &Path) -> bool {
199 &*self.0 == other
200 }
201}
202
203impl PartialOrd<Path> for ArcPath {
204 fn partial_cmp(&self, other: &Path) -> Option<std::cmp::Ordering> {
205 (**self.0).partial_cmp(other)
206 }
207}
208
209impl PartialEq<PathBuf> for ArcPath {
210 fn eq(&self, other: &PathBuf) -> bool {
211 &*self.0 == other
212 }
213}
214
215impl PartialOrd<PathBuf> for ArcPath {
216 fn partial_cmp(&self, other: &PathBuf) -> Option<std::cmp::Ordering> {
217 (**self.0).partial_cmp(other)
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
222#[bitflags]
223#[repr(u64)]
224pub enum Interest {
225 Established,
231 Any,
233 Access,
234 AccessOpen,
235 AccessClose,
236 AccessRead,
237 AccessOther,
238 Create,
239 CreateFile,
240 CreateFolder,
241 CreateOther,
242 Modify,
243 ModifyData,
244 ModifyDataSize,
245 ModifyDataContent,
246 ModifyDataOther,
247 ModifyMetadata,
248 ModifyMetadataAccessTime,
249 ModifyMetadataWriteTime,
250 ModifyMetadataPermissions,
251 ModifyMetadataOwnership,
252 ModifyMetadataExtended,
253 ModifyMetadataOther,
254 ModifyRename,
255 ModifyRenameTo,
256 ModifyRenameFrom,
257 ModifyRenameBoth,
258 ModifyRenameOther,
259 ModifyOther,
260 Delete,
261 DeleteFile,
262 DeleteFolder,
263 DeleteOther,
264 Other,
265}
266
267impl From<¬ify::EventKind> for Interest {
268 fn from(kind: ¬ify::EventKind) -> Self {
269 match kind {
270 notify::EventKind::Any => Self::Any,
271 notify::EventKind::Access(AccessKind::Any) => Self::Access,
272 notify::EventKind::Access(AccessKind::Close(_)) => Self::AccessClose,
273 notify::EventKind::Access(AccessKind::Open(_)) => Self::AccessOpen,
274 notify::EventKind::Access(AccessKind::Read) => Self::AccessRead,
275 notify::EventKind::Access(AccessKind::Other) => Self::AccessOther,
276 notify::EventKind::Create(CreateKind::Any) => Self::Create,
277 notify::EventKind::Create(CreateKind::File) => Self::CreateFile,
278 notify::EventKind::Create(CreateKind::Folder) => Self::CreateFolder,
279 notify::EventKind::Create(CreateKind::Other) => Self::CreateOther,
280 notify::EventKind::Modify(ModifyKind::Any) => Self::Modify,
281 notify::EventKind::Modify(ModifyKind::Data(DataChange::Any)) => {
282 Self::ModifyData
283 }
284 notify::EventKind::Modify(ModifyKind::Data(DataChange::Content)) => {
285 Self::ModifyDataContent
286 }
287 notify::EventKind::Modify(ModifyKind::Data(DataChange::Size)) => {
288 Self::ModifyDataSize
289 }
290 notify::EventKind::Modify(ModifyKind::Data(DataChange::Other)) => {
291 Self::ModifyDataOther
292 }
293 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any)) => {
294 Self::ModifyMetadata
295 }
296 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::AccessTime)) => {
297 Self::ModifyMetadataAccessTime
298 }
299 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Extended)) => {
300 Self::ModifyMetadataExtended
301 }
302 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Other)) => {
303 Self::ModifyMetadataOther
304 }
305 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Ownership)) => {
306 Self::ModifyMetadataOwnership
307 }
308 notify::EventKind::Modify(ModifyKind::Metadata(
309 MetadataKind::Permissions,
310 )) => Self::ModifyMetadataPermissions,
311 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::WriteTime)) => {
312 Self::ModifyMetadataWriteTime
313 }
314 notify::EventKind::Modify(ModifyKind::Name(RenameMode::Any)) => {
315 Self::ModifyRename
316 }
317 notify::EventKind::Modify(ModifyKind::Name(RenameMode::Both)) => {
318 Self::ModifyRenameBoth
319 }
320 notify::EventKind::Modify(ModifyKind::Name(RenameMode::From)) => {
321 Self::ModifyRenameFrom
322 }
323 notify::EventKind::Modify(ModifyKind::Name(RenameMode::To)) => {
324 Self::ModifyRenameTo
325 }
326 notify::EventKind::Modify(ModifyKind::Name(RenameMode::Other)) => {
327 Self::ModifyRenameOther
328 }
329 notify::EventKind::Modify(ModifyKind::Other) => Self::ModifyOther,
330 notify::EventKind::Remove(RemoveKind::Any) => Self::Delete,
331 notify::EventKind::Remove(RemoveKind::File) => Self::DeleteFile,
332 notify::EventKind::Remove(RemoveKind::Folder) => Self::DeleteFolder,
333 notify::EventKind::Remove(RemoveKind::Other) => Self::DeleteOther,
334 notify::EventKind::Other => Self::Other,
335 }
336 }
337}
338
339#[derive(Debug, Clone)]
340struct Watch {
341 path: ArcPath,
342 id: Id,
343 interest: BitFlags<Interest>,
344}
345
346impl Watch {
347 fn interested(&self, kind: ¬ify::EventKind) -> bool {
348 use Interest::*;
349 if self.interest.contains(Any) {
350 return true;
351 }
352 match kind {
353 notify::EventKind::Any => !self.interest.is_empty(),
354 notify::EventKind::Access(AccessKind::Any) => self
355 .interest
356 .intersects(Access | AccessClose | AccessOpen | AccessRead | AccessOther),
357 notify::EventKind::Access(AccessKind::Close(_)) => {
358 self.interest.intersects(Access | AccessClose)
359 }
360 notify::EventKind::Access(AccessKind::Open(_)) => {
361 self.interest.intersects(Access | AccessOpen)
362 }
363 notify::EventKind::Access(AccessKind::Read) => {
364 self.interest.intersects(Access | AccessRead)
365 }
366 notify::EventKind::Access(AccessKind::Other) => {
367 self.interest.intersects(Access | AccessOther)
368 }
369 notify::EventKind::Create(CreateKind::Any) => {
370 self.interest.intersects(Create | CreateFile | CreateFolder | CreateOther)
371 }
372 notify::EventKind::Create(CreateKind::File) => {
373 self.interest.intersects(Create | CreateFile)
374 }
375 notify::EventKind::Create(CreateKind::Folder) => {
376 self.interest.intersects(Create | CreateFolder)
377 }
378 notify::EventKind::Create(CreateKind::Other) => {
379 self.interest.intersects(Create | CreateOther)
380 }
381 notify::EventKind::Modify(ModifyKind::Any) => self.interest.intersects(
382 Modify
383 | ModifyData
384 | ModifyDataSize
385 | ModifyDataContent
386 | ModifyDataOther
387 | ModifyMetadata
388 | ModifyMetadataAccessTime
389 | ModifyMetadataWriteTime
390 | ModifyMetadataPermissions
391 | ModifyMetadataOwnership
392 | ModifyMetadataExtended
393 | ModifyMetadataOther
394 | ModifyRename
395 | ModifyRenameTo
396 | ModifyRenameFrom
397 | ModifyRenameBoth
398 | ModifyRenameOther
399 | ModifyOther,
400 ),
401 notify::EventKind::Modify(ModifyKind::Data(DataChange::Any)) => {
402 self.interest.intersects(
403 Modify
404 | ModifyData
405 | ModifyDataSize
406 | ModifyDataContent
407 | ModifyDataOther,
408 )
409 }
410 notify::EventKind::Modify(ModifyKind::Data(DataChange::Content)) => {
411 self.interest.intersects(Modify | ModifyData | ModifyDataContent)
412 }
413 notify::EventKind::Modify(ModifyKind::Data(DataChange::Size)) => {
414 self.interest.intersects(Modify | ModifyData | ModifyDataSize)
415 }
416 notify::EventKind::Modify(ModifyKind::Data(DataChange::Other)) => {
417 self.interest.intersects(Modify | ModifyData | ModifyDataOther)
418 }
419 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any)) => {
420 self.interest.intersects(
421 Modify
422 | ModifyMetadata
423 | ModifyMetadataAccessTime
424 | ModifyMetadataWriteTime
425 | ModifyMetadataPermissions
426 | ModifyMetadataOwnership
427 | ModifyMetadataExtended
428 | ModifyMetadataOther,
429 )
430 }
431 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::AccessTime)) => {
432 self.interest
433 .intersects(Modify | ModifyMetadata | ModifyMetadataAccessTime)
434 }
435 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Extended)) => {
436 self.interest.intersects(Modify | ModifyMetadata | ModifyMetadataExtended)
437 }
438 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Other)) => {
439 self.interest.intersects(Modify | ModifyMetadata | ModifyMetadataOther)
440 }
441 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::Ownership)) => {
442 self.interest
443 .intersects(Modify | ModifyMetadata | ModifyMetadataOwnership)
444 }
445 notify::EventKind::Modify(ModifyKind::Metadata(
446 MetadataKind::Permissions,
447 )) => self
448 .interest
449 .intersects(Modify | ModifyMetadata | ModifyMetadataPermissions),
450 notify::EventKind::Modify(ModifyKind::Metadata(MetadataKind::WriteTime)) => {
451 self.interest
452 .intersects(Modify | ModifyMetadata | ModifyMetadataWriteTime)
453 }
454 notify::EventKind::Modify(ModifyKind::Name(RenameMode::Any)) => {
455 self.interest.intersects(
456 Modify
457 | ModifyRename
458 | ModifyRenameTo
459 | ModifyRenameFrom
460 | ModifyRenameBoth
461 | ModifyRenameOther,
462 )
463 }
464 notify::EventKind::Modify(ModifyKind::Name(RenameMode::Both)) => {
465 self.interest.intersects(Modify | ModifyRename | ModifyRenameBoth)
466 }
467 notify::EventKind::Modify(ModifyKind::Name(RenameMode::From)) => {
468 self.interest.intersects(Modify | ModifyRename | ModifyRenameFrom)
469 }
470 notify::EventKind::Modify(ModifyKind::Name(RenameMode::To)) => {
471 self.interest.intersects(Modify | ModifyRename | ModifyRenameTo)
472 }
473 notify::EventKind::Modify(ModifyKind::Name(RenameMode::Other)) => {
474 self.interest.intersects(Modify | ModifyRename | ModifyRenameOther)
475 }
476 notify::EventKind::Modify(ModifyKind::Other) => {
477 self.interest.intersects(Modify | ModifyOther)
478 }
479 notify::EventKind::Remove(RemoveKind::Any) => {
480 self.interest.intersects(Delete | DeleteFile | DeleteFolder | DeleteOther)
481 }
482 notify::EventKind::Remove(RemoveKind::File) => {
483 self.interest.intersects(Delete | DeleteFile)
484 }
485 notify::EventKind::Remove(RemoveKind::Folder) => {
486 self.interest.intersects(Delete | DeleteFolder)
487 }
488 notify::EventKind::Remove(RemoveKind::Other) => {
489 self.interest.intersects(Delete | DeleteOther)
490 }
491 notify::EventKind::Other => self.interest.contains(Other),
492 }
493 }
494}
495
496#[derive(Debug)]
497enum Cmd {
498 Watch(Watch),
499 Stop(Id),
500 SetPollInterval(Duration),
501 SetPollBatch(usize),
502}
503
504struct NotifyChan(mpsc::Sender<DebounceEventResult>);
505
506impl DebounceEventHandler for NotifyChan {
507 fn handle_event(&mut self, event: notify_debouncer_full::DebounceEventResult) {
508 let _ = self.0.blocking_send(event);
509 }
510}
511
512#[derive(Debug, Clone)]
513pub enum EventKind {
514 Error(Arc<anyhow::Error>),
515 Event(Interest),
516}
517
518#[derive(Debug)]
519pub struct Event {
520 pub paths: GPooled<FxHashSet<ArcPath>>,
521 pub event: EventKind,
522}
523
524pub type EventBatch = GPooled<Vec<(Id, Event)>>;
526
527pub trait EventHandler: Send + 'static {
528 fn handle_event(
529 &mut self,
530 event: EventBatch,
531 ) -> impl Future<Output = Result<()>> + Send;
532}
533
534impl EventHandler for mpsc::Sender<EventBatch> {
535 fn handle_event(
536 &mut self,
537 event: EventBatch,
538 ) -> impl Future<Output = Result<()>> + Send {
539 async { Ok(self.send(event).await?) }
540 }
541}
542
543impl EventHandler for futures::channel::mpsc::Sender<EventBatch> {
544 fn handle_event(
545 &mut self,
546 event: EventBatch,
547 ) -> impl Future<Output = Result<()>> + Send {
548 use futures::SinkExt;
549 async { Ok(self.send(event).await?) }
550 }
551}
552
553#[derive(Debug)]
559pub struct Watched {
560 id: Id,
561 watcher: Watcher,
562}
563
564impl Watched {
565 pub fn id(&self) -> Id {
567 self.id
568 }
569}
570
571impl Borrow<Id> for Watched {
572 fn borrow(&self) -> &Id {
573 &self.id
574 }
575}
576
577impl<'a> Borrow<Id> for &'a Watched {
578 fn borrow(&self) -> &'a Id {
579 &self.id
580 }
581}
582
583impl PartialEq for Watched {
584 fn eq(&self, other: &Self) -> bool {
585 self.id == other.id
586 }
587}
588
589impl Eq for Watched {}
590
591impl PartialOrd for Watched {
592 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
593 Some(self.id.cmp(&other.id))
594 }
595}
596
597impl Ord for Watched {
598 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
599 self.id.cmp(&other.id)
600 }
601}
602
603impl Hash for Watched {
604 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
605 self.id.hash(state)
606 }
607}
608
609impl Drop for Watched {
610 fn drop(&mut self) {
611 let _ = self.watcher.0.send(Cmd::Stop(self.id));
612 }
613}
614
615#[derive(Debug, Clone, Builder)]
616pub struct WatcherConfig<T: EventHandler> {
617 #[builder(default = "Duration::from_millis(250)")]
619 timeout: Duration,
620 #[builder(setter(strip_option), default)]
622 tick_rate: Option<Duration>,
623 #[builder(default = "Duration::from_secs(1)")]
634 poll_interval: Duration,
635
636 #[builder(default = "100")]
641 poll_batch: usize,
642 event_handler: T,
644}
645
646impl<T: EventHandler> WatcherConfig<T> {
647 pub fn start(self) -> Result<Watcher> {
649 let (notify_tx, notify_rx) = mpsc::channel(MAX_NOTIFY_BATCH);
650 let watcher = notify_debouncer_full::new_debouncer(
651 self.timeout,
652 self.tick_rate,
653 NotifyChan(notify_tx),
654 )?;
655 let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
656 task::spawn(watch_task::watcher_loop(
657 self.poll_interval,
658 self.poll_batch,
659 watcher,
660 notify_rx,
661 cmd_rx,
662 self.event_handler,
663 ));
664 Ok(Watcher(cmd_tx))
665 }
666}
667
668#[derive(Debug, Clone)]
679pub struct Watcher(mpsc::UnboundedSender<Cmd>);
680
681impl Watcher {
682 pub fn add(&self, path: ArcPath, interest: BitFlags<Interest>) -> Result<Watched> {
692 let id = Id::new();
693 self.0.send(Cmd::Watch(Watch { path, interest, id }))?;
694 Ok(Watched { id, watcher: self.clone() })
695 }
696
697 pub fn set_poll_interval(&self, t: Duration) -> Result<()> {
708 if t < MIN_POLL_INTERVAL {
709 bail!("poll interval may not be less than {MIN_POLL_INTERVAL:?}")
710 }
711 Ok(self.0.send(Cmd::SetPollInterval(t))?)
712 }
713
714 pub fn set_poll_batch(&self, n: usize) -> Result<()> {
719 Ok(self.0.send(Cmd::SetPollBatch(n))?)
720 }
721}