1use std::fmt;
20
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct JsonRpcVersion;
31
32impl Default for JsonRpcVersion {
33 fn default() -> Self {
34 Self
35 }
36}
37
38impl fmt::Display for JsonRpcVersion {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.write_str("2.0")
41 }
42}
43
44impl Serialize for JsonRpcVersion {
45 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
46 serializer.serialize_str("2.0")
47 }
48}
49
50impl<'de> Deserialize<'de> for JsonRpcVersion {
51 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
52 struct VersionVisitor;
53
54 impl serde::de::Visitor<'_> for VersionVisitor {
55 type Value = JsonRpcVersion;
56
57 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.write_str("the string \"2.0\"")
59 }
60
61 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<JsonRpcVersion, E> {
62 if v == "2.0" {
63 Ok(JsonRpcVersion)
64 } else {
65 Err(E::custom(format!(
66 "expected JSON-RPC version \"2.0\", got \"{v}\""
67 )))
68 }
69 }
70 }
71
72 deserializer.deserialize_str(VersionVisitor)
73 }
74}
75
76pub type JsonRpcId = Option<serde_json::Value>;
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct JsonRpcRequest {
92 pub jsonrpc: JsonRpcVersion,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub id: JsonRpcId,
98
99 pub method: String,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub params: Option<serde_json::Value>,
105}
106
107impl JsonRpcRequest {
108 #[must_use]
110 pub fn new(id: serde_json::Value, method: impl Into<String>) -> Self {
111 Self {
112 jsonrpc: JsonRpcVersion,
113 id: Some(id),
114 method: method.into(),
115 params: None,
116 }
117 }
118
119 #[must_use]
121 pub fn with_params(
122 id: serde_json::Value,
123 method: impl Into<String>,
124 params: serde_json::Value,
125 ) -> Self {
126 Self {
127 jsonrpc: JsonRpcVersion,
128 id: Some(id),
129 method: method.into(),
130 params: Some(params),
131 }
132 }
133
134 #[must_use]
136 pub fn notification(method: impl Into<String>, params: Option<serde_json::Value>) -> Self {
137 Self {
138 jsonrpc: JsonRpcVersion,
139 id: None,
140 method: method.into(),
141 params,
142 }
143 }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
157#[serde(untagged)]
158pub enum JsonRpcResponse<T> {
159 Success(JsonRpcSuccessResponse<T>),
161 Error(JsonRpcErrorResponse),
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct JsonRpcSuccessResponse<T> {
170 pub jsonrpc: JsonRpcVersion,
172
173 pub id: JsonRpcId,
175
176 pub result: T,
178}
179
180impl<T> JsonRpcSuccessResponse<T> {
181 #[must_use]
183 pub const fn new(id: JsonRpcId, result: T) -> Self {
184 Self {
185 jsonrpc: JsonRpcVersion,
186 id,
187 result,
188 }
189 }
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct JsonRpcErrorResponse {
197 pub jsonrpc: JsonRpcVersion,
199
200 pub id: JsonRpcId,
203
204 pub error: JsonRpcError,
206}
207
208impl JsonRpcErrorResponse {
209 #[must_use]
211 pub const fn new(id: JsonRpcId, error: JsonRpcError) -> Self {
212 Self {
213 jsonrpc: JsonRpcVersion,
214 id,
215 error,
216 }
217 }
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct JsonRpcError {
225 pub code: i32,
227
228 pub message: String,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub data: Option<serde_json::Value>,
234}
235
236impl JsonRpcError {
237 #[must_use]
239 pub fn new(code: i32, message: impl Into<String>) -> Self {
240 Self {
241 code,
242 message: message.into(),
243 data: None,
244 }
245 }
246
247 #[must_use]
249 pub fn with_data(code: i32, message: impl Into<String>, data: serde_json::Value) -> Self {
250 Self {
251 code,
252 message: message.into(),
253 data: Some(data),
254 }
255 }
256}
257
258impl fmt::Display for JsonRpcError {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 write!(f, "[{}] {}", self.code, self.message)
261 }
262}
263
264impl std::error::Error for JsonRpcError {}
265
266#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn version_serializes_as_2_0() {
274 let v = JsonRpcVersion;
275 let s = serde_json::to_string(&v).expect("serialize");
276 assert_eq!(s, "\"2.0\"");
277 }
278
279 #[test]
280 fn version_rejects_wrong_version() {
281 let result: Result<JsonRpcVersion, _> = serde_json::from_str("\"1.0\"");
282 assert!(result.is_err(), "should reject non-2.0 version");
283 }
284
285 #[test]
286 fn version_accepts_2_0() {
287 let v: JsonRpcVersion = serde_json::from_str("\"2.0\"").expect("deserialize");
288 assert_eq!(v, JsonRpcVersion);
289 }
290
291 #[test]
292 fn request_roundtrip() {
293 let req = JsonRpcRequest::with_params(
294 serde_json::json!(1),
295 "message/send",
296 serde_json::json!({"message": {}}),
297 );
298 let json = serde_json::to_string(&req).expect("serialize");
299 assert!(json.contains("\"jsonrpc\":\"2.0\""));
300 assert!(json.contains("\"method\":\"message/send\""));
301
302 let back: JsonRpcRequest = serde_json::from_str(&json).expect("deserialize");
303 assert_eq!(back.method, "message/send");
304 }
305
306 #[test]
307 fn success_response_roundtrip() {
308 let resp: JsonRpcResponse<serde_json::Value> =
309 JsonRpcResponse::Success(JsonRpcSuccessResponse::new(
310 Some(serde_json::json!(42)),
311 serde_json::json!({"status": "ok"}),
312 ));
313 let json = serde_json::to_string(&resp).expect("serialize");
314 assert!(json.contains("\"result\""));
315 assert!(!json.contains("\"error\""));
316 }
317
318 #[test]
319 fn error_response_roundtrip() {
320 let resp: JsonRpcResponse<serde_json::Value> =
321 JsonRpcResponse::Error(JsonRpcErrorResponse::new(
322 Some(serde_json::json!(1)),
323 JsonRpcError::new(-32601, "Method not found"),
324 ));
325 let json = serde_json::to_string(&resp).expect("serialize");
326 assert!(json.contains("\"error\""));
327 assert!(json.contains("-32601"));
328 }
329
330 #[test]
331 fn notification_has_no_id() {
332 let n = JsonRpcRequest::notification("task/cancel", None);
333 let json = serde_json::to_string(&n).expect("serialize");
334 assert!(
335 !json.contains("\"id\""),
336 "notification must omit id: {json}"
337 );
338 }
339
340 #[test]
343 fn version_display() {
344 assert_eq!(JsonRpcVersion.to_string(), "2.0");
345 }
346
347 #[test]
348 #[allow(clippy::default_trait_access)]
349 fn version_default() {
350 let v: JsonRpcVersion = Default::default();
351 assert_eq!(v, JsonRpcVersion);
352 }
353
354 #[test]
355 fn version_rejects_non_string_types() {
356 assert!(serde_json::from_str::<JsonRpcVersion>("2.0").is_err());
358 assert!(serde_json::from_str::<JsonRpcVersion>("null").is_err());
360 assert!(serde_json::from_str::<JsonRpcVersion>("true").is_err());
362 assert!(serde_json::from_str::<JsonRpcVersion>("\"\"").is_err());
364 assert!(serde_json::from_str::<JsonRpcVersion>("\"2.1\"").is_err());
366 assert!(serde_json::from_str::<JsonRpcVersion>("\" 2.0\"").is_err());
367 }
368
369 #[test]
375 fn version_visitor_expecting_describes_2_0() {
376 let err =
377 serde_json::from_str::<JsonRpcVersion>("42").expect_err("must error on number input");
378 let msg = err.to_string();
379 assert!(
380 msg.contains("2.0"),
381 "expected error to describe expected value \"2.0\", got: {msg}"
382 );
383 }
384
385 #[test]
386 fn version_visitor_expecting_describes_string() {
387 let err =
388 serde_json::from_str::<JsonRpcVersion>("null").expect_err("must error on null input");
389 let msg = err.to_string();
390 assert!(
393 msg.contains("string") || msg.contains("2.0"),
394 "expected error mentioning 'string' or '2.0', got: {msg}"
395 );
396 }
397
398 #[test]
401 fn request_new_has_no_params() {
402 let req = JsonRpcRequest::new(serde_json::json!(1), "test/method");
403 assert_eq!(req.method, "test/method");
404 assert_eq!(req.id, Some(serde_json::json!(1)));
405 assert!(req.params.is_none());
406 assert_eq!(req.jsonrpc, JsonRpcVersion);
407 }
408
409 #[test]
410 fn request_with_params_has_params() {
411 let params = serde_json::json!({"key": "val"});
412 let req =
413 JsonRpcRequest::with_params(serde_json::json!("str-id"), "method", params.clone());
414 assert_eq!(req.params, Some(params));
415 assert_eq!(req.id, Some(serde_json::json!("str-id")));
416 }
417
418 #[test]
419 fn notification_has_method_and_params() {
420 let params = serde_json::json!({"task_id": "t1"});
421 let n = JsonRpcRequest::notification("task/cancel", Some(params.clone()));
422 assert!(n.id.is_none());
423 assert_eq!(n.method, "task/cancel");
424 assert_eq!(n.params, Some(params));
425 }
426
427 #[test]
430 fn jsonrpc_error_display() {
431 let e = JsonRpcError::new(-32600, "Invalid Request");
432 assert_eq!(e.to_string(), "[-32600] Invalid Request");
433 }
434
435 #[test]
436 fn jsonrpc_error_is_std_error() {
437 let e = JsonRpcError::new(-32600, "test");
438 let _: &dyn std::error::Error = &e;
439 }
440
441 #[test]
442 fn jsonrpc_error_new_has_no_data() {
443 let e = JsonRpcError::new(-32600, "test");
444 assert!(e.data.is_none());
445 assert_eq!(e.code, -32600);
446 assert_eq!(e.message, "test");
447 }
448
449 #[test]
450 fn jsonrpc_error_with_data_has_data() {
451 let data = serde_json::json!({"extra": true});
452 let e = JsonRpcError::with_data(-32601, "not found", data.clone());
453 assert_eq!(e.data, Some(data));
454 assert_eq!(e.code, -32601);
455 assert_eq!(e.message, "not found");
456 }
457
458 #[test]
461 fn success_response_fields() {
462 let resp = JsonRpcSuccessResponse::new(Some(serde_json::json!(1)), "ok");
463 assert_eq!(resp.id, Some(serde_json::json!(1)));
464 assert_eq!(resp.result, "ok");
465 assert_eq!(resp.jsonrpc, JsonRpcVersion);
466 }
467
468 #[test]
469 fn error_response_fields() {
470 let err = JsonRpcError::new(-32600, "bad");
471 let resp = JsonRpcErrorResponse::new(Some(serde_json::json!(2)), err);
472 assert_eq!(resp.id, Some(serde_json::json!(2)));
473 assert_eq!(resp.error.code, -32600);
474 assert_eq!(resp.jsonrpc, JsonRpcVersion);
475 }
476}