Aand I don't need a negotiation protocol. I need a registration hook, where the plugin gets a channel, and sends back a list of "IDs" it can handle. Whenever fuck encounters a top-level node, it runs it through the handleres registered for the node name.
And that's all fuck does. Any other names that aren't top level, are invoked by the top-level handlers.
And they all have the same shape:
fn call(&self, id: &str, node: &KdlNode) -> miette::Result<plugin::Response>
where plugin::Response is an enum:
pub enum Response {
Untouched,
Kdl(Vec<KdlNode>),
Derivation(Vec<Derivation>),
}
Something along those lines.
Eh. Or I could go full Fennel. Less boilerplate, more powerful config language, at the cost of far less useful errors.
I might be able to fix the errors later... perhaps by switching to tinylisp, or my own, that implements enough of the language to be useful.
The advantage of going full Fennel is that I'd have something faster, with less boilerplate necessary. Maybe that's worth it.
I mean, the chances I will rewrite everything from scratch again, before I start building the OS parts is pretty damn high anyway.