atcoder_auto_tester/
detector.rs1use 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}