LCOV - code coverage report
Current view: top level - dashql-cli/src/agent - protocol.rs Coverage Total Hit
Test: DashQL 6e74948 Lines: 94.1 % 34 32
Test Date: 2026-11-06 17:46:50

            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              : }
        

Generated by: LCOV version 2.0-1