modeldriveprotocol_client/
protocol.rs1use 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}