Skip to main content

wcode_protocol/
backend.rs

1//! A session handle that is local *or* remote.
2//!
3//! [`Backend`] is the seam the client code targets: the REPL, the TUI, and (S4)
4//! a peer session call `send`/`ask`/`subscribe` and never branch on whether the
5//! session runs in this process ([`SessionHandle`]) or across a socket
6//! ([`Client`]). Swapping the transport is swapping the variant.
7
8use tokio::sync::broadcast;
9use wcode_harness::actor::SessionHandle;
10use wcode_harness::event::AgentEvent;
11use wcode_harness::protocol::{Request, SessionId};
12
13#[cfg(unix)]
14use crate::client::Client;
15
16/// The session is gone — locally (the actor shut down) or remotely (the
17/// connection dropped).
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct Closed;
20
21impl std::fmt::Display for Closed {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        f.write_str("session is closed")
24    }
25}
26
27impl std::error::Error for Closed {}
28
29/// A handle to a session, local or remote.
30#[derive(Clone)]
31pub enum Backend {
32    /// In-process: the kernel actor.
33    Local(SessionHandle),
34    /// Across a socket: a remote client.
35    #[cfg(unix)]
36    Remote(Client),
37}
38
39impl Backend {
40    /// Fire-and-forget.
41    pub fn send(&self, request: Request) -> Result<(), Closed> {
42        match self {
43            Backend::Local(handle) => handle.send(request).map_err(|_| Closed),
44            #[cfg(unix)]
45            Backend::Remote(client) => client.send(request),
46        }
47    }
48
49    /// Fire-and-forget, attributed to `from` (A2A): the sender reaches the
50    /// target's `before_inbound` and sender tag, local or over a socket.
51    pub fn send_from(&self, from: SessionId, request: Request) -> Result<(), Closed> {
52        match self {
53            Backend::Local(handle) => handle.send_from(from, request).map_err(|_| Closed),
54            #[cfg(unix)]
55            Backend::Remote(client) => client.send_from(from, request),
56        }
57    }
58
59    /// Send and await the correlated reply.
60    pub async fn ask(&self, request: Request) -> Result<AgentEvent, Closed> {
61        match self {
62            Backend::Local(handle) => handle.ask(request).await.map_err(|_| Closed),
63            #[cfg(unix)]
64            Backend::Remote(client) => client.ask(request).await,
65        }
66    }
67
68    /// Stream the session's events.
69    pub fn subscribe(&self) -> broadcast::Receiver<AgentEvent> {
70        match self {
71            Backend::Local(handle) => handle.subscribe(),
72            #[cfg(unix)]
73            Backend::Remote(client) => client.subscribe(),
74        }
75    }
76}
77
78impl From<SessionHandle> for Backend {
79    fn from(handle: SessionHandle) -> Self {
80        Backend::Local(handle)
81    }
82}
83
84#[cfg(unix)]
85impl From<Client> for Backend {
86    fn from(client: Client) -> Self {
87        Backend::Remote(client)
88    }
89}