rsincron 0.0.3

Rust rewrite of the incredibly useful but abandoned incron software
Documentation
use std::{collections::HashMap, fs::read_to_string, process::Command};

use futures::TryStreamExt;
use inotify::{Inotify, WatchMask};
use rsincronlib::{get_user_table_path, EVENT_TYPES};

#[async_std::main]
async fn main() {
    let mut inotify = Inotify::init().expect("Error while initializing inotify instance");
    let user = std::env::var("USER").expect("USER is not set: exiting");

    let types = HashMap::from(EVENT_TYPES);
    let table = read_to_string(get_user_table_path().join(user))
        .expect("failed to read user table: exiting");

    let mut commands = HashMap::new();
    for line in table.lines() {
        if line.clone().chars().nth(0) == Some('#') {
            continue;
        };

        let mut fields = line.split('\t');
        let Some(path) = dbg!(fields.next()) else {
            continue;
        };

        let mask = {
            let Some(masks) = dbg!(fields.next()) else {
                continue;
            };

            masks.split(',').fold(WatchMask::empty(), |mut mask, new| {
                mask.insert(*types.get(new).unwrap());
                return mask;
            })
        };

        let Ok(descriptor) = dbg!(inotify.add_watch(path, mask)) else {
            continue;
        };

        let mut command = Command::new("bash");
        let Some(arguments) = dbg!(fields.next()) else {
            continue;
        };

        command.arg("-c");
        commands.entry(descriptor).or_insert((path, arguments));
    }

    let buffer = [0; 1024];
    let mut stream = inotify.event_stream(buffer).unwrap();

    while let Ok(event) = stream.try_next().await {
        let Some(event) = event else {
            continue;
        };

        println!("{event:#?}");
        let (path, arguments) = commands.get(&event.wd).unwrap();
        let filename = match event.name {
            Some(string) => string.to_str().unwrap_or_default().to_owned(),
            _ => String::default(),
        };

        let masks = format!("{:?}", event.mask).replace(" | ", ",");
        let arguments = {
            let mut formatted = String::new();
            let mut dollar = false;
            for c in arguments.chars() {
                if c == '$' {
                    if !dollar {
                        dollar = true;
                    } else {
                        formatted.push(c);
                        dollar = false;
                    }
                } else {
                    if dollar {
                        match c {
                            '#' => formatted.push_str(&filename),
                            '@' => formatted.push_str(&path.to_string()),
                            '%' => formatted.push_str(&masks),
                            '&' => formatted.push_str(&event.mask.bits().to_string()),
                            _ => (),
                        }
                        dollar = false;
                    } else {
                        formatted.push(c);
                    }
                }
            }
            formatted
        };

        let _ = Command::new("bash").arg("-c").arg(arguments).spawn();
    }
}