Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

An event loop instead of a trait

A frontend has a loop already, so cacp-events hands it one rather than a trait to implement. Every call from the agent arrives as an Event on a channel.

let (client, mut events) = cacp_events::channel();
let (agent, _child) = cacp::spawn(&mut Command::new("my-agent"), client)?;

while let Some(event) = events.recv().await {
    match event {
        Event::Update(notification) => draw(notification.update),
        Event::Permission(request, reply) => reply.send(ask_the_user(request).await),
        _ => {}
    }
}

A variant carrying a Reply is a request: answer it, or drop the reply to decline. Dropping sends method not found — exactly what the Client trait sends for a method you never implemented — so the _ => {} above serves nothing but updates and permission, and says so on the wire.

That transparency is the whole design. A consumer that ignores an Event is indistinguishable from one that never implemented that method, which is why the adapter needs no builder and no opt-in list.

What it inherits

Notifications reach the channel in wire order. A request is dispatched on its own task, so it can arrive just after a notification that followed it on the wire.

A Reply also knows when the agent gave up: is_cancelled() and cancelled().await fire when $/cancel_request aborts the request, which is the cue to take a permission prompt back off the screen.

When to implement Client directly instead

The channel is not always the shorter path. Answer permission by policy rather than by asking a human — auto-approve, or refuse everything — and routing it out to a loop and back is strictly more code than a two-method impl Client.