Running Multiple Rust Binaries When They Change With fswatch

January, 2023

TL;DR

I use this command to watch for new rust binary source files and run them whenever they are created or changed.

fswatch --event Updated -e ".*" -i "\.rs$" -0 . \
  | xargs -0 -I{} basename {} ".rs" \
  | xargs -I{} sh -c \
  'export RUNFILE="{}" && clear && cargo run --bin $RUNFILE'

(It also clears the console before running the file which helps me track what's happening.)

THE DETAILS

I'm making a bunch of Rust binaries with examples that are just a few lines long. There's going to be a few hundred of them. I don't want to do Cargo new for each one to avoid eating up a lot of space. Instead, I'm placing them all in a single project.

Running each one is a straight forward matter of calling cargo run with the --bin flag and the name. For example:

cargo run --bin source_name

The trick is that I want to run them automatically when I create or change a file. There's a cargo-watch tool for this kind of thing, but I haven't been able to figure out how to make it do what I want with multiple binaries.

The solution I came up with is to use fswatch and xargs to handle the functionality.

The first version of the code looked like this:

fswatch --event Updated -e ".*" -i "\.rs$" -0 . \
  | xargs -0 -I{} basename {} ".rs" \
  | xargs -I{}  cargo run --bin {}

Running that inside the src/bin directory fires off the run process whenever a new file is added or an existing one is changed.

(TODO: Write up all the details of all the parts of the code)

The cargo output can get verbose which can make it hard to distinguish one run from another. Adding a clear call took care of that with some extra gymnastics. The final version of the code looks like this (which is the same as the start of the article)

fswatch --event Updated -e ".*" -i "\.rs$" -0 . \
  | xargs -0 -I{} basename {} ".rs" \
  | xargs -I{} sh -c \
  'export RUNFILE="{}" && clear && cargo run --bin $RUNFILE'

(TODO: Write up all the details of all the code)

I'm not sure how to pass the source_name through clear via | pipes. The solution I'm using for that is to use a shell where I set an environmental variable that gets read by cargo after clear wipes everything off the terminal.

It took some time to come up with that. And, frankly, I was nervous about trying to do it for reasons I can't really identify. But, I'm super glad I did. It's another example of working on something new that I'm uncomfortable with (xargs, in this case, specifically) and ended up with something that I'm really happy with and happy I powered through.