Skip to main content

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}