atcoder_auto_tester/
detector.rs

1use std::env;
2use std::thread;
3use std::time;
4
5use inotify::{EventMask, Events, Inotify, WatchMask};
6use itertools::Itertools;
7
8use crate::error::ErrorHandleable;
9
10pub struct Detector {
11    inotify: Inotify,
12    buffer: [u8; 4096],
13}
14
15impl Default for Detector {
16    fn default() -> Self {
17        let mut inotify = Inotify::init().handle_error("Failed to initialize inotify");
18        let current_dir = env::current_dir().handle_error("Failed to determine current directory");
19        inotify
20            .add_watch(current_dir, WatchMask::MODIFY | WatchMask::CREATE)
21            .handle_error("Failed to add inotify watch");
22        Self {
23            inotify,
24            buffer: [0u8; 4096],
25        }
26    }
27}
28
29impl Detector {
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    pub fn wait_file_names(&mut self) -> Vec<String> {
35        let mut file_names: Vec<String> = Self::wait_events(&mut self.inotify, &mut self.buffer)
36            .filter_map(|e| Self::extract_file_name(&e))
37            .collect();
38        if file_names.is_empty() {
39            self.wait_file_names()
40        } else {
41            thread::sleep(time::Duration::from_millis(200));
42            file_names.extend(
43                Self::get_events(&mut self.inotify, &mut self.buffer)
44                    .filter_map(|e| Self::extract_file_name(&e)),
45            );
46            file_names.into_iter().unique().collect()
47        }
48    }
49
50    fn file_changed(event: &inotify::Event<&std::ffi::OsStr>) -> bool {
51        (event.mask.contains(EventMask::CREATE) || event.mask.contains(EventMask::MODIFY))
52            && !event.mask.contains(EventMask::ISDIR)
53    }
54
55    fn extract_file_name(event: &inotify::Event<&std::ffi::OsStr>) -> Option<String> {
56        if Self::file_changed(&event) {
57            event.name.map(|name| String::from(name.to_string_lossy()))
58        } else {
59            None
60        }
61    }
62
63    fn wait_events<'a>(inotify: &'a mut Inotify, buffer: &'a mut [u8]) -> Events<'a> {
64        inotify
65            .read_events_blocking(buffer)
66            .handle_error("Failed to read inotify events")
67    }
68
69    fn get_events<'a>(inotify: &'a mut Inotify, buffer: &'a mut [u8]) -> Events<'a> {
70        inotify
71            .read_events(buffer)
72            .handle_error("Failed to read inotify events")
73    }
74}
75
76impl IntoIterator for Detector {
77    type Item = String;
78    type IntoIter = FileGen;
79
80    fn into_iter(self) -> Self::IntoIter {
81        FileGen::new(self)
82    }
83}
84
85pub struct FileGen {
86    detector: Detector,
87    file_names: Vec<String>,
88}
89
90impl FileGen {
91    pub fn new(detector: Detector) -> Self {
92        Self {
93            detector,
94            file_names: vec![],
95        }
96    }
97}
98
99impl Iterator for FileGen {
100    type Item = String;
101
102    fn next(&mut self) -> Option<Self::Item> {
103        if self.file_names.is_empty() {
104            self.file_names = self.detector.wait_file_names();
105        }
106        self.file_names.pop()
107    }
108}