A Simple Debouncer For Rust's notify File Watcher

Heads-Up

I've upgraded the way I do this, I'm not using the code from this page:

id: 2uxj96li

TL;DR

This is the initial Rust code I set up for my static site generator to rebuild the site when files change.

Code
use notify::{RecursiveMode, Watcher};
use notify_debouncer_full::new_debouncer;
use std::path::PathBuf;
use std::time::Duration;
use std::time::SystemTime;

pub fn main() {
    let path = PathBuf::from("/Users/alan/Desktop");
    let _ = watch_files(path);
}

fn watch_files(path: PathBuf) -> notify::Result<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    let mut debouncer = new_debouncer(Duration::from_millis(500), None, tx)?;

    debouncer.watcher().watch(&path, RecursiveMode::Recursive)?;
    debouncer.cache().add_root(&path, RecursiveMode::Recursive);

    let mut last_update = SystemTime::now();
    let debounce_time = Duration::from_secs(4);

    for _result in rx {
        if last_update.elapsed().unwrap() > debounce_time {
            do_update();
            last_update = SystemTime::now();
        }
    }

    Ok(())
}

fn do_update() {
    dbg!("Change detected");
}

Details

I shipped the first version of my static site generator. I've very happy with it and with being able to post again. Now that it builds it needs to build without me pressing buttons every time. To make that happen I'm using Rust's `notify`` crate.

The notify create watches the file system and triggers when events happen. There's a debounce feature to help prevent spamming but it only works on individual files. So, if you have 1,400 files update on your website it would trigger 1,400 builds.

Oops.

I added a simple debouncer on top on the one that comes with notify to prevent that. It checks a time stamp to see if more than 4 seconds have passed since that last build when a file change is detected. If that's the case, it sends the signal to build and updates the time stamp. Otherwise it does nothing.

I spent half a day trying to build a more sophisticated method. Then it clicked that I really don't need anything more that this.

Notes
  • I could probably pull out the notify debouncer. I think mine would cover anything that changed. I'll look into that if I refactor and setup to cover files that change in after the first one.

  • It would be better if the trigger for the rebuild happened at the end of the file updates. Otherwise the build may happen while files are still updating.

    The reason that matters is because the process I'm currently using to copy files to my site from my grimoire sends all 1,400 every time.

  • Another option would be to add a delay to the build process so it takes a bit of time to start after receiving the signal. Something else to look into.

  • Though, the ideal would be to figure out how to setup a timer for the debounce directly. That's what I spent the day trying to do but I don't know enough rust yet to make that happen.

  • I'm making a new version of this with some more logic in it. Specifically, if I edit a single file, only that file will be rebuilt. If I edit a template, it updates all the files. I'll write that one up too.

Reference

This is the page where I got most of the code

Reference

Reference

Reference

Reference

Keep these for writing up the larger one that wathces specifci files

// https://doc.rust-lang.org/std/time/struct.SystemTime.html // https://docs.rs/notify/latest/notify/event/enum.EventKind.html // https://docs.rs/notify/latest/notify/event/enum.ModifyKind.html