The words Under construction in black text on a yellow background with diagonal black stipes surrounding it
I'm in the process of moving my site. It's still a work in progress. Please excuse the mess and broken links.

Watch For File Changes In A Rust App

TODO: Pull subtitle into page object

This works, but there's a better way to do it where you just look at the paths direclty to see what's changed that way:

2uxj96li

This was an attempt to debounce. I though it was mostly working but that turns out to be less of the case than I originally thought. Leaving this here as a mark of progress, but more work needs to be done for the solution.

It's good enough for what I need for now though

Though... now it looks like the issue I'm seeing is because a child process keeps running? I don't know enough about how this works yet.

Proceed with caution.

-<

This is what I'm doing to watch for file changes to trigger re-renders in my blog engine. It uses the watchexec^we^^ crate.

Code
use core::fmt::Error;
use core::time::Duration;
use miette::Result;
use std::path::PathBuf;
use watchexec::{
    action::Action,
    action::Outcome,
    config::{InitConfig, RuntimeConfig},
    Watchexec,
};
use watchexec_events::filekind::FileEventKind;
use watchexec_events::filekind::ModifyKind;
use watchexec_events::Tag;

#[tokio::main]
async fn main() -> Result<()> {
    println!("Starting process");
    let init = InitConfig::default();
    let mut runtime = RuntimeConfig::default();
    runtime.pathset(["/Users/alan/Desktop"]);
    runtime.action_throttle(Duration::new(0, 100000));
    let we = Watchexec::new(init, runtime.clone())?;
    runtime.on_action(move |action: Action| {
        async move {
            let mut events: Vec<PathBuf> = vec![];
            for event in action.events.iter() {
                let mut trigger: bool = false;
                let mut file_path: Option<PathBuf> = None;
                event.tags.iter().for_each(|tag| match tag {
                    Tag::Path { path, .. } => {
                        file_path = Some(path.to_path_buf());
                        events.push(file_path.clone().unwrap());
                    }
                    Tag::FileEventKind(event_kind) => match event_kind {
                        FileEventKind::Create(_) => {
                            trigger = true;
                        }
                        FileEventKind::Modify(modify_kind) => match modify_kind {
                            ModifyKind::Data(_) => {
                                trigger = true;
                            }
                            _ => {}
                        },
                        _ => {}
                    },
                    _ => {}
                });
            }
            events.dedup();
            events.iter().for_each(|p| do_something(p.to_path_buf()));
            action.outcome(Outcome::DoNothing);
            Ok::<(), Error>(())
        }
    });
    we.reconfigure(runtime).unwrap();
    we.main().await.unwrap().unwrap();
    Ok(())
}

fn do_something(file_path: PathBuf) {
    dbg!(file_path);
}

Notes

Debugging Stuff

I'm moving stuff around right now. All this below is helping me figure out where to put stuff

        -- title

Watch For File Changes In A Rust App

-- warning 

This works, but there's a better way to do it
where you just look at the paths direclty 
to see what's changed that way: 

2uxj96li



-- note

This was an attempt to debounce. I though it
was mostly working but that turns out to be
less of the case than I originally thought. 
Leaving this here as a mark of progress, but
more work needs to be done for the solution.

It's good enough for what I need for now though

Though... now it looks like the issue I'm seeing
is because a child process keeps running? I 
don't know enough about how this works yet.

Proceed with caution.

->

This is what I'm doing to watch for file changes
to trigger re-renders in my blog engine. It uses
the watchexec^we^^ crate.

-- code
-- rust

use core::fmt::Error;
use core::time::Duration;
use miette::Result;
use std::path::PathBuf;
use watchexec::{
    action::Action,
    action::Outcome,
    config::{InitConfig, RuntimeConfig},
    Watchexec,
};
use watchexec_events::filekind::FileEventKind;
use watchexec_events::filekind::ModifyKind;
use watchexec_events::Tag;

#[tokio::main]
async fn main() -> Result<()> {
    println!("Starting process");
    let init = InitConfig::default();
    let mut runtime = RuntimeConfig::default();
    runtime.pathset(["/Users/alan/Desktop"]);
    runtime.action_throttle(Duration::new(0, 100000));
    let we = Watchexec::new(init, runtime.clone())?;
    runtime.on_action(move |action: Action| {
        async move {
            let mut events: Vec<PathBuf> = vec![];
            for event in action.events.iter() {
                let mut trigger: bool = false;
                let mut file_path: Option<PathBuf> = None;
                event.tags.iter().for_each(|tag| match tag {
                    Tag::Path { path, .. } => {
                        file_path = Some(path.to_path_buf());
                        events.push(file_path.clone().unwrap());
                    }
                    Tag::FileEventKind(event_kind) => match event_kind {
                        FileEventKind::Create(_) => {
                            trigger = true;
                        }
                        FileEventKind::Modify(modify_kind) => match modify_kind {
                            ModifyKind::Data(_) => {
                                trigger = true;
                            }
                            _ => {}
                        },
                        _ => {}
                    },
                    _ => {}
                });
            }
            events.dedup();
            events.iter().for_each(|p| do_something(p.to_path_buf()));
            action.outcome(Outcome::DoNothing);
            Ok::<(), Error>(())
        }
    });
    we.reconfigure(runtime).unwrap();
    we.main().await.unwrap().unwrap();
    Ok(())
}

fn do_something(file_path: PathBuf) {
    dbg!(file_path);
}


-- notes

- I'm still new to Rust so don't be surpirsed if there's
weird looking stuff up there. 

- This is using the `Watchexec::new()`` approach which is more
is designed to run external commands, but it works fine for
internal usage for me

- I've got other examples to post from my archive that
show the more low level approach but I moved to this
becuase it was easier to figure out how to setup a
semi-debounce

- The `.action_throttle()`rust` uses a `Duration`rust` which
is a second followed by nanoseconds. The goal with this 
approach is to throttle event togehter every 100ms to
act as a debounce. I'm not confident that it's setup
in an optimal way or even that it's doing what I think 
it is, but it's doing the job I want

- The way the throttle works starts the clock on the first
event that comes in from a group. It doesn't keep 
getting pushed out like it a standard debounce. For 
my site's content engine that doesn't matter

- It should be possible to set things up directly without
the `.reconfigure()`. This is built off an example that
used that approach so I'm sticking with it for sake
of getting something out the door



-- ref
-- id: we
-- title: watchexec
-- url: https://docs.rs/watchexec/latest/watchexec/index.html

-- categories
-- Rust 

-- metadata
-- date: 2023-04-27 18:57:45
-- id: 2p1t9jxc
-- site: aws
-- type: post
-- status: draft