1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use serde_with::skip_serializing_none;
9
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
15pub enum Version {
16 #[default]
17 #[serde(rename = "2.0")]
18 V2,
19}
20
21#[skip_serializing_none]
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Request {
25 #[serde(default)]
26 pub jsonrpc: Version,
27 #[serde(default)]
28 pub id: Option<Value>,
29 pub method: String,
30 #[serde(default)]
31 pub params: Option<Value>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Response {
37 #[serde(default)]
38 pub jsonrpc: Version,
39 pub id: Value,
40 #[serde(flatten)]
41 pub body: Body,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(rename_all = "lowercase")]
50pub enum Body {
51 Result(Value),
52 Error(Error),
53}
54
55#[skip_serializing_none]
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct Error {
59 pub code: i32,
60 pub message: String,
61 #[serde(default)]
62 pub data: Option<Value>,
63}
64
65impl Response {
66 pub fn success(id: Value, result: Value) -> Self {
68 Self {
69 jsonrpc: Version::V2,
70 id,
71 body: Body::Result(result),
72 }
73 }
74
75 pub fn error(id: Value, code: i32, message: impl Into<String>) -> Self {
77 Self {
78 jsonrpc: Version::V2,
79 id,
80 body: Body::Error(Error {
81 code,
82 message: message.into(),
83 data: None,
84 }),
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use serde_json::json;
93
94 #[test]
95 fn response_success_roundtrip() {
96 let resp = Response::success(json!(1), json!({"tools": []}));
97 let json = serde_json::to_value(&resp).unwrap();
98 assert_eq!(json["jsonrpc"], "2.0");
99 assert_eq!(json["id"], 1);
100 assert!(json.get("result").is_some());
101 assert!(json.get("error").is_none());
102 }
103
104 #[test]
105 fn response_error_roundtrip() {
106 let resp = Response::error(json!(1), -32601, "not found");
107 let json = serde_json::to_value(&resp).unwrap();
108 assert_eq!(json["error"]["code"], -32601);
109 assert_eq!(json["error"]["message"], "not found");
110 assert!(json.get("result").is_none());
111 }
112
113 #[test]
114 fn error_omits_null_data() {
115 let resp = Response::error(json!(1), -32600, "bad");
116 let json = serde_json::to_string(&resp).unwrap();
117 assert!(!json.contains("\"data\""));
118 }
119
120 #[test]
121 fn request_omits_null_fields() {
122 let req = Request {
123 jsonrpc: Version::V2,
124 id: None,
125 method: "ping".to_string(),
126 params: None,
127 };
128 let json = serde_json::to_string(&req).unwrap();
129 assert!(!json.contains("\"id\""));
130 assert!(!json.contains("\"params\""));
131 }
132
133 #[test]
134 fn version_rejects_invalid() {
135 let result: Result<Version, _> = serde_json::from_value(json!("1.0"));
136 assert!(result.is_err());
137 }
138
139 #[test]
140 fn version_default() {
141 assert_eq!(Version::default(), Version::V2);
142 }
143}