Line data Source code
1 : //! The messages between the agent server and its clients, dashql.app tabs and desktop windows.
2 : //! The JSON Schema in proto/json/dashql/agent_bridge.json defines the messages: AgentServerMethods names the params
3 : //! and results of the requests of clients, AgentClientMethods the requests and notifications of the server.
4 : //! The page generates its types from the schema, the tests of the server check its messages against it.
5 : //!
6 : //! Messages are JSON-RPC-style: a request {id, method, params} is answered by {id, result} or {id, error}.
7 : //! A message with a method but without an id is a notification.
8 : //!
9 : //! The command line tool calls the server over HTTP: `GET /v1/status` returns `AgentStatusResponse`,
10 : //! `POST /v1/call` takes `AgentCallRequest` and returns `AgentCallResponse`.
11 :
12 : use serde_json::{json, Value};
13 :
14 : /// The version of the protocol, a client with another version is turned away
15 : pub const PROTOCOL_VERSION: u64 = 2;
16 :
17 : /// The path of the WebSocket endpoint
18 : pub const WEBSOCKET_PATH: &str = "/v1/ws";
19 :
20 : /// The error codes of failed calls
21 : #[derive(Clone, Copy, Debug, PartialEq, Eq)]
22 : pub enum ErrorCode {
23 : /// The command failed
24 : Failed,
25 : /// No client holds the target session
26 : NotConnected,
27 : /// Another call of the session is running
28 : Busy,
29 : /// The call is malformed
30 : Usage,
31 : }
32 :
33 : impl ErrorCode {
34 15 : pub fn name(self) -> &'static str {
35 15 : match self {
36 8 : ErrorCode::Failed => "failed",
37 1 : ErrorCode::NotConnected => "not_connected",
38 1 : ErrorCode::Busy => "busy",
39 5 : ErrorCode::Usage => "usage",
40 : }
41 15 : }
42 :
43 : /// Unknown codes are failures
44 3 : pub fn parse(name: &str) -> Self {
45 3 : match name {
46 3 : "not_connected" => ErrorCode::NotConnected,
47 2 : "busy" => ErrorCode::Busy,
48 1 : "usage" => ErrorCode::Usage,
49 1 : _ => ErrorCode::Failed,
50 : }
51 3 : }
52 : }
53 :
54 : /// The error of a failed call
55 : #[derive(Clone, Debug, PartialEq, Eq)]
56 : pub struct RpcError {
57 : pub code: ErrorCode,
58 : pub message: String,
59 : }
60 :
61 : impl RpcError {
62 27 : pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
63 27 : RpcError { code, message: message.into() }
64 27 : }
65 :
66 15 : pub fn to_json(&self) -> Value {
67 15 : json!({ "code": self.code.name(), "message": self.message })
68 15 : }
69 :
70 : /// The error of a response, defaults for missing fields
71 1 : pub fn from_json(error: &Value) -> Self {
72 : RpcError {
73 1 : code: ErrorCode::parse(error["code"].as_str().unwrap_or_default()),
74 1 : message: match &error["message"] {
75 1 : Value::String(s) => s.clone(),
76 0 : Value::Null => "the command failed".into(),
77 0 : other => other.to_string(),
78 : },
79 : }
80 1 : }
81 :
82 : /// The response of a failed call
83 7 : pub fn to_call_response(&self) -> Value {
84 7 : json!({ "ok": false, "error": self.to_json() })
85 7 : }
86 : }
87 :
88 : #[cfg(test)]
89 : mod tests {
90 : use super::*;
91 : use crate::agent::schema::BridgeSchema;
92 :
93 : #[test]
94 1 : fn names_the_protocol_version_of_the_schema() {
95 1 : let params = BridgeSchema::get().definition("ClientSessionsParams").unwrap();
96 1 : assert_eq!(params["properties"]["protocol"]["const"], json!(PROTOCOL_VERSION));
97 1 : }
98 : }
|