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)]
154#[serde(untagged)]
155pub enum JsonRpcResponse<T> {
156 Success(JsonRpcSuccessResponse<T>),
158 Error(JsonRpcErrorResponse),
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct JsonRpcSuccessResponse<T> {
167 pub jsonrpc: JsonRpcVersion,
169
170 pub id: JsonRpcId,
172
173 pub result: T,
175}
176
177impl<T> JsonRpcSuccessResponse<T> {
178 #[must_use]
180 pub const fn new(id: JsonRpcId, result: T) -> Self {
181 Self {
182 jsonrpc: JsonRpcVersion,
183 id,
184 result,
185 }
186 }
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct JsonRpcErrorResponse {
194 pub jsonrpc: JsonRpcVersion,
196
197 pub id: JsonRpcId,
200
201 pub error: JsonRpcError,
203}
204
205impl JsonRpcErrorResponse {
206 #[must_use]
208 pub const fn new(id: JsonRpcId, error: JsonRpcError) -> Self {
209 Self {
210 jsonrpc: JsonRpcVersion,
211 id,
212 error,
213 }
214 }
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct JsonRpcError {
222 pub code: i32,
224
225 pub message: String,
227
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub data: Option<serde_json::Value>,
231}
232
233impl JsonRpcError {
234 #[must_use]
236 pub fn new(code: i32, message: impl Into<String>) -> Self {
237 Self {
238 code,
239 message: message.into(),
240 data: None,
241 }
242 }
243
244 #[must_use]
246 pub fn with_data(code: i32, message: impl Into<String>, data: serde_json::Value) -> Self {
247 Self {
248 code,
249 message: message.into(),
250 data: Some(data),
251 }
252 }
253}
254
255impl fmt::Display for JsonRpcError {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 write!(f, "[{}] {}", self.code, self.message)
258 }
259}
260
261impl std::error::Error for JsonRpcError {}
262
263#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn version_serializes_as_2_0() {
271 let v = JsonRpcVersion;
272 let s = serde_json::to_string(&v).expect("serialize");
273 assert_eq!(s, "\"2.0\"");
274 }
275
276 #[test]
277 fn version_rejects_wrong_version() {
278 let result: Result<JsonRpcVersion, _> = serde_json::from_str("\"1.0\"");
279 assert!(result.is_err(), "should reject non-2.0 version");
280 }
281
282 #[test]
283 fn version_accepts_2_0() {
284 let v: JsonRpcVersion = serde_json::from_str("\"2.0\"").expect("deserialize");
285 assert_eq!(v, JsonRpcVersion);
286 }
287
288 #[test]
289 fn request_roundtrip() {
290 let req = JsonRpcRequest::with_params(
291 serde_json::json!(1),
292 "message/send",
293 serde_json::json!({"message": {}}),
294 );
295 let json = serde_json::to_string(&req).expect("serialize");
296 assert!(json.contains("\"jsonrpc\":\"2.0\""));
297 assert!(json.contains("\"method\":\"message/send\""));
298
299 let back: JsonRpcRequest = serde_json::from_str(&json).expect("deserialize");
300 assert_eq!(back.method, "message/send");
301 }
302
303 #[test]
304 fn success_response_roundtrip() {
305 let resp: JsonRpcResponse<serde_json::Value> =
306 JsonRpcResponse::Success(JsonRpcSuccessResponse::new(
307 Some(serde_json::json!(42)),
308 serde_json::json!({"status": "ok"}),
309 ));
310 let json = serde_json::to_string(&resp).expect("serialize");
311 assert!(json.contains("\"result\""));
312 assert!(!json.contains("\"error\""));
313 }
314
315 #[test]
316 fn error_response_roundtrip() {
317 let resp: JsonRpcResponse<serde_json::Value> =
318 JsonRpcResponse::Error(JsonRpcErrorResponse::new(
319 Some(serde_json::json!(1)),
320 JsonRpcError::new(-32601, "Method not found"),
321 ));
322 let json = serde_json::to_string(&resp).expect("serialize");
323 assert!(json.contains("\"error\""));
324 assert!(json.contains("-32601"));
325 }
326
327 #[test]
328 fn notification_has_no_id() {
329 let n = JsonRpcRequest::notification("task/cancel", None);
330 let json = serde_json::to_string(&n).expect("serialize");
331 assert!(
332 !json.contains("\"id\""),
333 "notification must omit id: {json}"
334 );
335 }
336
337 #[test]
340 fn version_display() {
341 assert_eq!(JsonRpcVersion.to_string(), "2.0");
342 }
343
344 #[test]
345 #[allow(clippy::default_trait_access)]
346 fn version_default() {
347 let v: JsonRpcVersion = Default::default();
348 assert_eq!(v, JsonRpcVersion);
349 }
350
351 #[test]
352 fn version_rejects_non_string_types() {
353 assert!(serde_json::from_str::<JsonRpcVersion>("2.0").is_err());
355 assert!(serde_json::from_str::<JsonRpcVersion>("null").is_err());
357 assert!(serde_json::from_str::<JsonRpcVersion>("true").is_err());
359 assert!(serde_json::from_str::<JsonRpcVersion>("\"\"").is_err());
361 assert!(serde_json::from_str::<JsonRpcVersion>("\"2.1\"").is_err());
363 assert!(serde_json::from_str::<JsonRpcVersion>("\" 2.0\"").is_err());
364 }
365
366 #[test]
369 fn request_new_has_no_params() {
370 let req = JsonRpcRequest::new(serde_json::json!(1), "test/method");
371 assert_eq!(req.method, "test/method");
372 assert_eq!(req.id, Some(serde_json::json!(1)));
373 assert!(req.params.is_none());
374 assert_eq!(req.jsonrpc, JsonRpcVersion);
375 }
376
377 #[test]
378 fn request_with_params_has_params() {
379 let params = serde_json::json!({"key": "val"});
380 let req =
381 JsonRpcRequest::with_params(serde_json::json!("str-id"), "method", params.clone());
382 assert_eq!(req.params, Some(params));
383 assert_eq!(req.id, Some(serde_json::json!("str-id")));
384 }
385
386 #[test]
387 fn notification_has_method_and_params() {
388 let params = serde_json::json!({"task_id": "t1"});
389 let n = JsonRpcRequest::notification("task/cancel", Some(params.clone()));
390 assert!(n.id.is_none());
391 assert_eq!(n.method, "task/cancel");
392 assert_eq!(n.params, Some(params));
393 }
394
395 #[test]
398 fn jsonrpc_error_display() {
399 let e = JsonRpcError::new(-32600, "Invalid Request");
400 assert_eq!(e.to_string(), "[-32600] Invalid Request");
401 }
402
403 #[test]
404 fn jsonrpc_error_is_std_error() {
405 let e = JsonRpcError::new(-32600, "test");
406 let _: &dyn std::error::Error = &e;
407 }
408
409 #[test]
410 fn jsonrpc_error_new_has_no_data() {
411 let e = JsonRpcError::new(-32600, "test");
412 assert!(e.data.is_none());
413 assert_eq!(e.code, -32600);
414 assert_eq!(e.message, "test");
415 }
416
417 #[test]
418 fn jsonrpc_error_with_data_has_data() {
419 let data = serde_json::json!({"extra": true});
420 let e = JsonRpcError::with_data(-32601, "not found", data.clone());
421 assert_eq!(e.data, Some(data));
422 assert_eq!(e.code, -32601);
423 assert_eq!(e.message, "not found");
424 }
425
426 #[test]
429 fn success_response_fields() {
430 let resp = JsonRpcSuccessResponse::new(Some(serde_json::json!(1)), "ok");
431 assert_eq!(resp.id, Some(serde_json::json!(1)));
432 assert_eq!(resp.result, "ok");
433 assert_eq!(resp.jsonrpc, JsonRpcVersion);
434 }
435
436 #[test]
437 fn error_response_fields() {
438 let err = JsonRpcError::new(-32600, "bad");
439 let resp = JsonRpcErrorResponse::new(Some(serde_json::json!(2)), err);
440 assert_eq!(resp.id, Some(serde_json::json!(2)));
441 assert_eq!(resp.error.code, -32600);
442 assert_eq!(resp.jsonrpc, JsonRpcVersion);
443 }
444}