1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4use futures_util::StreamExt;
5use inotify::{EventMask, EventStream, Inotify, WatchDescriptor, WatchMask};
6use path_absolutize::*;
7
8#[derive(Debug)]
10pub struct Watcher {
11 stream: EventStream<[u8; 1024]>,
13
14 wds: HashMap<WatchDescriptor, PathBuf>,
16}
17
18impl Watcher {
19 pub fn init() -> Self {
21 let inotify = Inotify::init().expect("Failed to initialize Inotify");
22 let buffer = [0; 1024];
23 let stream = inotify.into_event_stream(buffer).unwrap();
24 Watcher {
25 stream,
26 wds: HashMap::new(),
27 }
28 }
29
30 pub fn add<P>(&mut self, path: P, mask: &WatchMask) -> crate::Result<WatchDescriptor>
32 where
33 P: AsRef<Path> + std::convert::AsRef<std::ffi::OsStr>,
34 {
35 let root = Path::new(&path).absolutize()?;
37 if !root.exists() {
38 return Err(format!("Error: {:?} is not exists", root).into());
39 }
40
41 let wd = self.stream.watches().add(&root, mask.clone())?;
42 self.wds.insert(wd.clone(), root.to_path_buf());
43 Ok(wd)
44 }
45
46 pub fn remove(&mut self, wd: WatchDescriptor) -> crate::Result<()> {
48 self.wds.remove(&wd);
49 self.stream.watches().remove(wd)?;
50 Ok(())
51 }
52
53 pub async fn next(&mut self) -> Option<Event> {
55 match self.stream.next().await {
56 Some(event) => {
58 let inotify::Event {
59 wd,
60 mask,
61 cookie: _,
62 name,
63 } = event.unwrap();
64
65 let mut root = self.wds.get(&wd).unwrap().clone();
66 if let Some(name) = name {
67 root.push(name);
68 }
69
70 return Some(Event { wd, root, mask });
71 }
72 _ => return None,
73 }
74 }
75}
76
77#[derive(Clone, Debug, PartialEq)]
78pub struct Event {
79 wd: WatchDescriptor,
80 root: PathBuf,
81 mask: EventMask,
82}
83
84impl Event {
85 pub fn wd(&self) -> &WatchDescriptor {
87 &self.wd
88 }
89
90 pub fn path(&self) -> &Path {
92 &self.root
93 }
94
95 pub fn mask(&self) -> &EventMask {
97 &self.mask
98 }
99
100 pub fn watchmask(&self) -> WatchMask {
102 WatchMask::from_bits(&self.mask.bits() & WatchMask::ALL_EVENTS.bits()).unwrap()
103 }
104
105 pub fn is_dir(&self) -> bool {
107 !(self.mask & EventMask::ISDIR).is_empty()
108 }
109
110 pub fn is_new(&self) -> bool {
112 !(self.mask & (EventMask::CREATE | EventMask::MOVED_FROM)).is_empty()
113 }
114
115 pub fn removed(&self) -> bool {
117 !(self.mask & EventMask::IGNORED).is_empty()
118 }
119}