Last Friday I spotted Dave Gauer's post about using a text editor as a UI which hit some of my sweet spots about computers. One of the examples mentioned in it was crontab -e which opens a cron config file in a text editor. And not only it spares you remembering the location of the config, but it also offers a guiding commented example if the config is missing, and helpfully signals cron to pick up the changes after you finish editing.
And almost immediately I thought about my own tool that could use something like this: nfp.
Amazingly enough, not only had I actually opened the project and started fiddling around, I continued doing it through the weekend, and by the end of day on Sunday actually finished the feature! And despite it being a rather small one, as they go, I have to add that I was coding all through watching snooker matches, cooking food, chauffeuring my family on errands and dealing with some emergencies.
So I went to bed feeling quite happy with myself :-)
Complications
It still feels exciting to me how any programming task gradually reveals its true complexity after you go from thinking about what you should do to actually doing it. Saying "nfp -e should open the config in a text editor and restart after editing" sounds simple enough, but here's a few of the questions I had to work through. Some of them were quite the head-scratchers.
-
First, open which editor? There's an obvious
$EDITORenv var, but there's also$VISUAL, which usually takes precedence. -
How do you restart? Sending a signal to a working daemon was the first thing that came to my mind, but that might prove cumbersome, as the daemon lives in a loop waiting for file events, and this loop owns the information parsed from the config. Handling
SIGHUPwould require a separate facility to update that information. I'm not quite comfortable thinking of how to do that in Rust.Thankfully, this complication turned out to be a blessing in disguise: since the file watching machinery is already there, just watch the config too and add a special case to handle it differently from regular files!
-
Do you edit the config file directly or do you do it on the side, in a temp file? The temp file feels like a cleaner, safer choice, because it gives you a chance to verify correctness of the new config and prevent the real one from breaking. But there's a downside: how do you open the same temp file for the user to continue editing it the next time they run
nfp -e? It's going to be a new process, it doesn't know the old temp file.crontab -edoes it by organizing a loop within the same process with a yes/no prompt asking the user if they want to re-edit the same file. But that starts feeling more complicated than the feature deserves.I ended up with a simpler solution where I always open the actual config, which means it can get mangled. I handle it in the running daemon itself, which simply refuses to restart its main loop when it can't parse the config.
-
Providing an example config on the first run proved to be tricky, as confy (the config handling library) actually immediately creates a non-empty file if it doesn't exist on the a load attempt. So I had to rewire my brain to think "a config with no useful entries" instead of "a missing config." That worked!
All in all, this was quite fun!
Some admin notes
I (finally) converted the repository from pijul to git and pushed it to CodeBerg. I still think pijul has a superior architecture as a VCS, but the world has apparently settled on git for good. Also, while I'm happy to not deal with the toxic culture of GitHub, having code published in a weird way means most people wouldn't even want to try it. After 4 years I haven't gotten a single peep of feedback :-) And I still believe in sharing. I hope CodeBerg becomes my sweet spot.
Keep in mind though, that I don't code in Rust regularly and don't keep up with modern idioms. So my code is most certainly very ripe for various improvements. I'd love to hear from you! (An obvious one is, the code needs tests. I just need to learn how to write them in Rust!)