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 98 99 100 101 102 103 104 105 106 107 108
use std::env; use std::thread; use std::time; use inotify::{EventMask, Events, Inotify, WatchMask}; use itertools::Itertools; use crate::error::ErrorHandleable; pub struct Detector { inotify: Inotify, buffer: [u8; 4096], } impl Default for Detector { fn default() -> Self { let mut inotify = Inotify::init().handle_error("Failed to initialize inotify"); let current_dir = env::current_dir().handle_error("Failed to determine current directory"); inotify .add_watch(current_dir, WatchMask::MODIFY | WatchMask::CREATE) .handle_error("Failed to add inotify watch"); Self { inotify, buffer: [0u8; 4096], } } } impl Detector { pub fn new() -> Self { Self::default() } pub fn wait_file_names(&mut self) -> Vec<String> { let mut file_names: Vec<String> = Self::wait_events(&mut self.inotify, &mut self.buffer) .filter_map(|e| Self::extract_file_name(&e)) .collect(); if file_names.is_empty() { self.wait_file_names() } else { thread::sleep(time::Duration::from_millis(200)); file_names.extend( Self::get_events(&mut self.inotify, &mut self.buffer) .filter_map(|e| Self::extract_file_name(&e)), ); file_names.into_iter().unique().collect() } } fn file_changed(event: &inotify::Event<&std::ffi::OsStr>) -> bool { (event.mask.contains(EventMask::CREATE) || event.mask.contains(EventMask::MODIFY)) && !event.mask.contains(EventMask::ISDIR) } fn extract_file_name(event: &inotify::Event<&std::ffi::OsStr>) -> Option<String> { if Self::file_changed(&event) { event.name.map(|name| String::from(name.to_string_lossy())) } else { None } } fn wait_events<'a>(inotify: &'a mut Inotify, buffer: &'a mut [u8]) -> Events<'a> { inotify .read_events_blocking(buffer) .handle_error("Failed to read inotify events") } fn get_events<'a>(inotify: &'a mut Inotify, buffer: &'a mut [u8]) -> Events<'a> { inotify .read_events(buffer) .handle_error("Failed to read inotify events") } } impl IntoIterator for Detector { type Item = String; type IntoIter = FileGen; fn into_iter(self) -> Self::IntoIter { FileGen::new(self) } } pub struct FileGen { detector: Detector, file_names: Vec<String>, } impl FileGen { pub fn new(detector: Detector) -> Self { Self { detector, file_names: vec![], } } } impl Iterator for FileGen { type Item = String; fn next(&mut self) -> Option<Self::Item> { if self.file_names.is_empty() { self.file_names = self.detector.wait_file_names(); } self.file_names.pop() } }