1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

#![crate_name = "patrol"]
#![crate_type = "lib"]

extern crate inotify;

use std::collections::{HashSet, HashMap};
use std::path::PathBuf;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread;

use inotify::INotify;
use inotify::ffi::*;


#[derive(Debug)]
pub struct Target {
    pub path: PathBuf,
    pub is_file: bool,
}


impl Target {
    pub fn new(path: &str) -> Target {
        let path = PathBuf::from(path);
        let is_file = path.is_file();
        Target { path: path, is_file: is_file }
    }

    fn watch_path(&self) -> PathBuf {
        if self.is_file {
            self.path.parent().expect("Invalid path found").to_path_buf()
        } else {
            self.path.clone()
        }
    }
}


pub struct Event;


const EVENTS: u32 = IN_CREATE | IN_MODIFY | IN_DELETE;


pub fn make_targets(targets: &[&str]) -> Vec<Target> {
    targets.iter().map(|it| Target::new(it)).collect()
}


pub fn spawn(targets: Vec<Target>) -> Receiver<Event> {
    let (tx, rx) = channel();
    thread::spawn(move || start(targets, tx));
    rx
}


pub fn start(targets: Vec<Target>, sender: Sender<Event>) {
    let mut ino = INotify::init().unwrap();

    let mut watched: HashMap<PathBuf, i32> = HashMap::new();

    let mut directories: HashSet<i32> = HashSet::new();
    let mut files: HashMap<i32, HashSet<String>> = HashMap::new();

    for target in targets {
        let watch_path = target.watch_path();
        let wd = watched.entry(watch_path.clone()).or_insert_with(|| {
            ino.add_watch(watch_path.as_path(), EVENTS).unwrap()
        });
        if target.is_file {
            let mut files = files.entry(*wd).or_insert_with(|| {
                HashSet::new()
            });
            files.insert(target.path.file_name().unwrap().to_str().unwrap().to_string());
        } else {
            directories.insert(*wd);
        }
    }

    loop {
        let events = ino.wait_for_events().unwrap();

        for event in events.iter() {
            if !event.is_dir() {
                let wd = event.wd;
                if directories.contains(&wd) {
                    sender.send(Event).unwrap();
                } else if let Some(files) = files.get_mut(&wd) {
                    if files.contains(event.name.to_str().unwrap()) {
                        sender.send(Event).unwrap();
                    }
                }
            }
        }
    }
}