I am trying to build a cli tool to transpile a file into another in Rust.
I'd like for this tool to wait for a change in the input file to automatically trasnspile it again to the output file.
Ideally I would write something like this
loop {
if hasChanged(input_file) {
transpile(input_file, output_file, settings);
}
}
Where the hasChanged
function is blocking
to avoid a busy loop
But the only library that I found that watches if a file has changed is notify, that uses a callback for dealing with file events.
(Taken from the library page)
fn event_fn(res: Result<notify::Event>) {
match res {
Ok(event) => println!("event: {:?}", event),
Err(e) => println!("watch error: {:?}", e),
}
}
mut watcher1 = notify::recommended_watcher(event_fn)?;
The problem arises from the fact that inside the callback I cannot access the settings that i have parsed from the cli arguments, nor the input_file
(a simple String
) or the output_file
(a Box<dyn Write>
use to abstract an actual file or stdout).
Also, since all I'm doing is waiting for the file to change, after the callback setup my main function just ends, and I don't want the program to just terminate there.
Is the only solution to do all the work (parsing arguments and opening files) inside the callback?
Is there a way to pass the arguments (especially the Box<dyn Write>
) to the callback in any other way?
Or there is another library that fits my use case better?
This is my first project in rust so I'm not familiar with its threading rules and lifetimes. Thanks.
The answer is on the docs page of notify
that you linked:
From the docs:
use notify::{recommended_watcher, Event, RecursiveMode, Result, Watcher};
use std::sync::mpsc;
fn main() -> Result<()> {
let (tx, rx) = mpsc::channel::<Result<Event>>();
// Use recommended_watcher() to automatically select the best implementation
// for your platform. The `EventHandler` passed to this constructor can be a
// closure, a `std::sync::mpsc::Sender`, a `crossbeam_channel::Sender`, or
// another type the trait is implemented for.
let mut watcher = notify::recommended_watcher(tx)?;
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
watcher.watch(Path::new("."), RecursiveMode::Recursive)?;
// Block forever, printing out events as they come in
for res in rx {
match res {
Ok(event) => println!("event: {:?}", event),
Err(e) => println!("watch error: {:?}", e),
}
}
Ok(())
}
This example uses an multi-producer-single-consumer channel to feed events from the callback into your main loop, while avoiding a busy loop.
From within the match res
statement, you should have access to any variables you created earlier in your main
function, which should include your config and input/output files.