Skip to main content

modeldriveprotocol_client/
protocol.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4use crate::error::MdpClientError;
5use crate::models::{AuthContext, ClientDescriptor, HttpMethod, SerializedError};
6use crate::path_utils::is_concrete_path;
7
8#[derive(Clone, Debug, PartialEq, Serialize)]
9#[serde(tag = "type")]
10pub enum ClientToServerMessage {
11    #[serde(rename = "registerClient")]
12    RegisterClient {
13        client: ClientDescriptor,
14        #[serde(skip_serializing_if = "Option::is_none")]
15        auth: Option<AuthContext>,
16    },
17    #[serde(rename = "updateClientCatalog")]
18    UpdateClientCatalog {
19        #[serde(rename = "clientId")]
20        client_id: String,
21        paths: Vec<crate::models::PathDescriptor>,
22    },
23    #[serde(rename = "unregisterClient")]
24    UnregisterClient {
25        #[serde(rename = "clientId")]
26        client_id: String,
27    },
28    #[serde(rename = "callClientResult")]
29    CallClientResult {
30        #[serde(rename = "requestId")]
31        request_id: String,
32        ok: bool,
33        #[serde(skip_serializing_if = "Option::is_none")]
34        data: Option<Value>,
35        #[serde(skip_serializing_if = "Option::is_none")]
36        error: Option<SerializedError>,
37    },
38    #[serde(rename = "pong")]
39    Pong { timestamp: i64 },
40}
41
42#[derive(Clone, Debug, PartialEq)]
43pub enum ServerToClientMessage {
44    CallClient(CallClientRequest),
45    Ping { timestamp: i64 },
46    Pong { timestamp: i64 },
47}
48
49#[derive(Clone, Debug, PartialEq, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub struct CallClientRequest {
52    pub request_id: String,
53    pub client_id: String,
54    pub method: HttpMethod,
55    pub path: String,
56    pub params: Option<Map<String, Value>>,
57    pub query: Option<Map<String, Value>>,
58    pub body: Option<Value>,
59    pub headers: Option<std::collections::HashMap<String, String>>,
60    pub auth: Option<AuthContext>,
61}
62
63impl ServerToClientMessage {
64    pub fn from_text(raw: &str) -> Result<Self, MdpClientError> {
65        let value: Value = serde_json::from_str(raw)?;
66        Self::from_value(value)
67    }
68
69    pub fn from_value(value: Value) -> Result<Self, MdpClientError> {
70        let object = value
71            .as_object()
72            .ok_or_else(|| MdpClientError::Protocol("invalid MDP message payload".to_string()))?;
73
74        let message_type = object
75            .get("type")
76            .and_then(Value::as_str)
77            .ok_or_else(|| MdpClientError::Protocol("missing message type".to_string()))?;
78
79        match message_type {
80            "ping" => Ok(Self::Ping {
81                timestamp: object
82                    .get("timestamp")
83                    .and_then(Value::as_i64)
84                    .ok_or_else(|| MdpClientError::Protocol("invalid ping payload".to_string()))?,
85            }),
86            "pong" => Ok(Self::Pong {
87                timestamp: object
88                    .get("timestamp")
89                    .and_then(Value::as_i64)
90                    .ok_or_else(|| MdpClientError::Protocol("invalid pong payload".to_string()))?,
91            }),
92            "callClient" => {
93                let request: CallClientRequest = serde_json::from_value(value)?;
94                if !is_concrete_path(&request.path) {
95                    return Err(MdpClientError::Protocol("invalid callClient path".to_string()));
96                }
97                Ok(Self::CallClient(request))
98            }
99            other => Err(MdpClientError::Protocol(format!(
100                "unsupported server message type `{other}`"
101            ))),
102        }
103    }
104}