I’m working on my composable desktop workflow orchestration CLI toolbox (hope you enjoy my buzzword salad) called bmo, which is my eternal hobby project which also drives most interaction with my i3 desktop such as quick opening files, URL’s, command launch memory, state switching (and transition handling), terminal opening and hacker style dmenu-driven custom menus.
The bmo toolset itself is pretty niche (and far from presentable form) but the learning is really fun because writing it as just a CLI could make my life easier, I’ve decided to treat it as API to give myself a “dojo” to try out what works and what does not when working across multiple levels on the stack frame: how to manage memory, contexts, lifetimes, and my recent “enemy” - error payload handling. And since bmo makes heavy use of several of my own Zig dependencies (eg. zig-bareini), I can work on both sides of the API contract.
Past week I’ve been focusing on error payload handling.
First, I’m a stickler for helpful error messages. For example, for config errors I always want to report details like file path and line/column numbers, but do so regardless of whether the error comes from tokenization (inside my zig-bareini lib) or it’s domain of the specific tool (ie. semantically invalid or ambiguous config options).
Earier version of bmo would make this relatively simple, since in most cases simply std.log + std.process.exit while the error details are still fresh would be enough. Treating it as API, however, is more fun.
Recently I’ve been adopting Diagnostic pattern, where I require API user to instantiate a Diag struct and pass pointer to it, and specific error code then signifies that details have been saved, and the diag itself can help with the reporting and even outlive the context of the API call. However, it becomes more tricky once I start to combine various diagnostic structs and still want to preserve ergonomics.
I was literally in the middle of figuring this out when Andrew announced this thread on Libera
. So back to work!

Edit: I’m still doing all this on 0.15.2. Once I get something that kind of works well, I can’t wait to start updating my code to 0.16.0.