async_inotify/
watcher.rs

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/// A Watcher contains EventStream,maintained a `HashMap` about `WatchDescriptor` to `PathBuf`
9#[derive(Debug)]
10pub struct Watcher {
11    // An Inotfiy::EventStream
12    stream: EventStream<[u8; 1024]>,
13
14    // Use this Map to get real Path by wd.
15    wds: HashMap<WatchDescriptor, PathBuf>,
16}
17
18impl Watcher {
19    // init a new Watcher
20    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    // Add a new path with watchmask to Watcher.
31    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        // check path exists
36        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    // remove a watch by WatchDescriptor
47    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    // Async call for Watched events.
54    pub async fn next(&mut self) -> Option<Event> {
55        match self.stream.next().await {
56            // 获取事件,转换为 `Watcher::Event`
57            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    // Show the watched fd's WatchDescriptor
86    pub fn wd(&self) -> &WatchDescriptor {
87        &self.wd
88    }
89
90    // The full file path about the event
91    pub fn path(&self) -> &Path {
92        &self.root
93    }
94
95    // Show raw EventMask
96    pub fn mask(&self) -> &EventMask {
97        &self.mask
98    }
99
100    // Show the mask as WatchMask
101    pub fn watchmask(&self) -> WatchMask {
102        WatchMask::from_bits(&self.mask.bits() & WatchMask::ALL_EVENTS.bits()).unwrap()
103    }
104
105    // Is directory event
106    pub fn is_dir(&self) -> bool {
107        !(self.mask & EventMask::ISDIR).is_empty()
108    }
109
110    // Is new (create/moved_from) file
111    pub fn is_new(&self) -> bool {
112        !(self.mask & (EventMask::CREATE | EventMask::MOVED_FROM)).is_empty()
113    }
114
115    // Is watched fd be removed
116    pub fn removed(&self) -> bool {
117        !(self.mask & EventMask::IGNORED).is_empty()
118    }
119}