oino_client/backend.rs
1//! The [`SessionBackend`] contract every frontend talks to (architecture.md ยง4.2).
2
3use futures::future::BoxFuture;
4use futures::stream::BoxStream;
5use oino_core::events::ClientEvent;
6use oino_core::ui::UiResponse;
7
8use crate::protocol::{RpcCommand, RpcResponseData};
9
10/// Failure of a backend command.
11#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
12#[non_exhaustive]
13pub enum ClientError {
14 /// The command failed; the text is the error message (RPC `error`).
15 #[error("{0}")]
16 Command(String),
17 /// The backend is gone (session disposed, connection closed).
18 #[error("backend closed")]
19 Closed,
20 /// Transport failure (spawn, I/O).
21 #[error("transport: {0}")]
22 Transport(String),
23 /// The peer sent something that is not a valid record.
24 #[error("protocol: {0}")]
25 Protocol(String),
26}
27
28/// One live session as a frontend sees it: commands in, one ordered event stream out.
29pub trait SessionBackend: Send + Sync {
30 /// Any agent session or `_oino/*` command; returns the response `data`.
31 fn send(&self, command: RpcCommand) -> BoxFuture<'_, Result<RpcResponseData, ClientError>>;
32
33 /// [`SessionBackend::send`] with the RPC request id; `bash` streams
34 /// `bash_execution_update{id}` with it (the command id goes to `executeBash`).
35 fn send_with_id(
36 &self,
37 request_id: Option<String>,
38 command: RpcCommand,
39 ) -> BoxFuture<'_, Result<RpcResponseData, ClientError>> {
40 let _ = request_id;
41 self.send(command)
42 }
43
44 /// Ordered event stream for this session (a new subscription each call).
45 fn events(&self) -> BoxStream<'static, ClientEvent>;
46
47 /// Answers a dialog (`extension_ui_response`); idempotent, first answer wins.
48 fn ui_respond(&self, response: UiResponse);
49
50 /// Current session id.
51 fn session_id(&self) -> String;
52
53 /// Orderly shutdown (`session_shutdown{reason:"quit"}`); later commands fail with
54 /// [`ClientError::Closed`].
55 fn shutdown(&self) -> BoxFuture<'_, ()>;
56
57 /// Resolves once an extension asked the frontend to exit (`ctx.shutdown()`, event
58 /// `shutdown_requested`) and the session is idle; frontends then shut down and exit.
59 /// Never resolves by default.
60 fn shutdown_requested(&self) -> BoxFuture<'static, ()> {
61 Box::pin(futures::future::pending())
62 }
63}