1use serde::{Deserialize, Serialize};
6use serde_json::Value as JsonValue;
7use std::{string::String, vec::Vec};
8
9#[allow(dead_code)]
11pub const PROTOCOL_VERSION: &str = "1.0";
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct HelloMessage {
16 pub version: String,
18
19 pub client: String,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub capabilities: Option<Vec<String>>,
25
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub auth_token: Option<String>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct WelcomeMessage {
34 pub version: String,
36
37 pub server: String,
39
40 pub permissions: Vec<String>,
42
43 pub writable_records: Vec<String>,
45
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub max_subscriptions: Option<usize>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub authenticated: Option<bool>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct Request {
58 pub id: u64,
60
61 pub method: String,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub params: Option<JsonValue>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71#[serde(untagged)]
72pub enum Response {
73 Success {
75 id: u64,
77 result: JsonValue,
79 },
80 Error {
82 id: u64,
84 error: ErrorObject,
86 },
87}
88
89impl Response {
90 pub fn success(id: u64, result: JsonValue) -> Self {
92 Self::Success { id, result }
93 }
94
95 pub fn error(id: u64, code: impl Into<String>, message: impl Into<String>) -> Self {
97 Self::Error {
98 id,
99 error: ErrorObject {
100 code: code.into(),
101 message: message.into(),
102 details: None,
103 },
104 }
105 }
106
107 pub fn error_with_details(
109 id: u64,
110 code: impl Into<String>,
111 message: impl Into<String>,
112 details: JsonValue,
113 ) -> Self {
114 Self::Error {
115 id,
116 error: ErrorObject {
117 code: code.into(),
118 message: message.into(),
119 details: Some(details),
120 },
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct ErrorObject {
128 pub code: String,
130
131 pub message: String,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub details: Option<JsonValue>,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct Event {
142 pub subscription_id: String,
144
145 pub sequence: u64,
147
148 pub data: JsonValue,
150
151 pub timestamp: String,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub dropped: Option<u64>,
157}
158
159#[allow(dead_code)] #[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(untagged)]
163pub enum Message {
164 Hello { hello: HelloMessage },
166 Welcome { welcome: WelcomeMessage },
168 Request(Request),
170 Response(Response),
172 Event { event: Event },
174}
175
176#[allow(dead_code)] impl Message {
178 pub fn hello(client: impl Into<String>) -> Self {
180 Self::Hello {
181 hello: HelloMessage {
182 version: PROTOCOL_VERSION.to_string(),
183 client: client.into(),
184 capabilities: None,
185 auth_token: None,
186 },
187 }
188 }
189
190 pub fn welcome(server: impl Into<String>, permissions: Vec<String>) -> Self {
192 Self::Welcome {
193 welcome: WelcomeMessage {
194 version: PROTOCOL_VERSION.to_string(),
195 server: server.into(),
196 permissions,
197 writable_records: Vec::new(),
198 max_subscriptions: None,
199 authenticated: None,
200 },
201 }
202 }
203
204 pub fn request(id: u64, method: impl Into<String>, params: Option<JsonValue>) -> Self {
206 Self::Request(Request {
207 id,
208 method: method.into(),
209 params,
210 })
211 }
212
213 pub fn response_success(id: u64, result: JsonValue) -> Self {
215 Self::Response(Response::success(id, result))
216 }
217
218 pub fn response_error(id: u64, code: impl Into<String>, message: impl Into<String>) -> Self {
220 Self::Response(Response::error(id, code, message))
221 }
222
223 pub fn event(
225 subscription_id: impl Into<String>,
226 sequence: u64,
227 data: JsonValue,
228 timestamp: impl Into<String>,
229 ) -> Self {
230 Self::Event {
231 event: Event {
232 subscription_id: subscription_id.into(),
233 sequence,
234 data,
235 timestamp: timestamp.into(),
236 dropped: None,
237 },
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_hello_serialization() {
248 let hello = HelloMessage {
249 version: "1.0".to_string(),
250 client: "test-client".to_string(),
251 capabilities: Some(vec!["read".to_string()]),
252 auth_token: None,
253 };
254
255 let json = serde_json::to_string(&hello).unwrap();
256 assert!(json.contains("\"version\":\"1.0\""));
257 assert!(json.contains("\"client\":\"test-client\""));
258 }
259
260 #[test]
261 fn test_request_serialization() {
262 let request = Request {
263 id: 1,
264 method: "record.list".to_string(),
265 params: Some(serde_json::json!({})),
266 };
267
268 let json = serde_json::to_string(&request).unwrap();
269 assert!(json.contains("\"id\":1"));
270 assert!(json.contains("\"method\":\"record.list\""));
271 }
272
273 #[test]
274 fn test_response_success() {
275 let response = Response::success(1, serde_json::json!({"status": "ok"}));
276
277 let json = serde_json::to_string(&response).unwrap();
278 assert!(json.contains("\"id\":1"));
279 assert!(json.contains("\"result\""));
280 assert!(json.contains("\"status\":\"ok\""));
281 }
282
283 #[test]
284 fn test_response_error() {
285 let response = Response::error(2, "NOT_FOUND", "Record not found");
286
287 let json = serde_json::to_string(&response).unwrap();
288 assert!(json.contains("\"id\":2"));
289 assert!(json.contains("\"error\""));
290 assert!(json.contains("\"code\":\"NOT_FOUND\""));
291 assert!(json.contains("\"message\":\"Record not found\""));
292 }
293
294 #[test]
295 fn test_event_serialization() {
296 let event = Event {
297 subscription_id: "sub-123".to_string(),
298 sequence: 42,
299 data: serde_json::json!({"temp": 23.5}),
300 timestamp: "1730379296.123456789".to_string(),
301 dropped: None,
302 };
303
304 let json = serde_json::to_string(&event).unwrap();
305 assert!(json.contains("\"subscription_id\":\"sub-123\""));
306 assert!(json.contains("\"sequence\":42"));
307 assert!(json.contains("\"temp\":23.5"));
308 }
309
310 #[test]
311 fn test_event_with_dropped() {
312 let event = Event {
313 subscription_id: "sub-456".to_string(),
314 sequence: 100,
315 data: serde_json::json!({"value": 1}),
316 timestamp: "1730379300.987654321".to_string(),
317 dropped: Some(5),
318 };
319
320 let json = serde_json::to_string(&event).unwrap();
321 assert!(json.contains("\"dropped\":5"));
322 }
323}