#![feature(exit_status_error)]
use std::collections::HashMap;
use std::error::Error;
use std::ffi::{OsStr, OsString};
use std::sync::Arc;
use std::time::Duration;
use debounce::MixedEventDebouncer;
use inotify::{Event, EventMask, Inotify, WatchDescriptor};
mod config;
mod processor;
#[derive(PartialEq, Eq)]
struct File {
wd: WatchDescriptor,
name: OsString,
}
impl From<Event<&OsStr>> for File {
fn from(event: Event<&OsStr>) -> Self {
File {
wd: event.wd,
name: event.name.unwrap().to_owned(),
}
}
}
fn run(inotify: &mut Inotify) -> Result<(), Box<dyn Error>> {
let processors: Arc<HashMap<_, _>> = Arc::new(
config::load()?
.processors
.iter()
.flat_map(|p| {
p.paths().map(move |path| processor::Processor {
path,
command: p.command.clone(),
args: p.args.clone(),
debounce: p.debounce,
keep_file: p.keep_file,
})
})
.filter_map(|p| p.register(inotify).map(|d| (d, p)))
.collect(),
);
let debouncer = {
let processors = processors.clone();
MixedEventDebouncer::new(move |file: File| {
let processor = &processors[&file.wd];
let filename = processor.path.join(&file.name);
println!("Processing {}", filename.display());
processor
.process(&filename)
.unwrap_or_else(|e| eprintln!("Error: {}", e));
})
};
let mut buffer = [0; 1024];
let mut cookie = u32::MAX;
loop {
for event in inotify.read_events_blocking(&mut buffer)? {
match event.mask {
m if m.contains(EventMask::ISDIR) => return Ok(()),
EventMask::MOVED_FROM => cookie = event.cookie,
m if m.intersects(EventMask::CLOSE_WRITE | EventMask::MOVED_TO)
&& cookie != event.cookie =>
{
let delay = Duration::from_millis(processors[&event.wd].debounce);
debouncer.put(event.into(), delay)
}
_ => (),
}
}
}
}
fn main() -> Result<(), Box<dyn Error>> {
let mut inotify = Inotify::init()?;
loop {
run(&mut inotify)?
}
}