1use std::fmt;
18
19use serde::{Deserialize, Deserializer, Serialize, Serializer};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct JsonRpcVersion;
29
30impl Default for JsonRpcVersion {
31 fn default() -> Self {
32 Self
33 }
34}
35
36impl fmt::Display for JsonRpcVersion {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 f.write_str("2.0")
39 }
40}
41
42impl Serialize for JsonRpcVersion {
43 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
44 serializer.serialize_str("2.0")
45 }
46}
47
48impl<'de> Deserialize<'de> for JsonRpcVersion {
49 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
50 let s = String::deserialize(deserializer)?;
51 if s == "2.0" {
52 Ok(Self)
53 } else {
54 Err(serde::de::Error::custom(format!(
55 "expected JSON-RPC version \"2.0\", got \"{s}\""
56 )))
57 }
58 }
59}
60
61pub type JsonRpcId = Option<serde_json::Value>;
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct JsonRpcRequest {
77 pub jsonrpc: JsonRpcVersion,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub id: JsonRpcId,
83
84 pub method: String,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub params: Option<serde_json::Value>,
90}
91
92impl JsonRpcRequest {
93 #[must_use]
95 pub fn new(id: serde_json::Value, method: impl Into<String>) -> Self {
96 Self {
97 jsonrpc: JsonRpcVersion,
98 id: Some(id),
99 method: method.into(),
100 params: None,
101 }
102 }
103
104 #[must_use]
106 pub fn with_params(
107 id: serde_json::Value,
108 method: impl Into<String>,
109 params: serde_json::Value,
110 ) -> Self {
111 Self {
112 jsonrpc: JsonRpcVersion,
113 id: Some(id),
114 method: method.into(),
115 params: Some(params),
116 }
117 }
118
119 #[must_use]
121 pub fn notification(method: impl Into<String>, params: Option<serde_json::Value>) -> Self {
122 Self {
123 jsonrpc: JsonRpcVersion,
124 id: None,
125 method: method.into(),
126 params,
127 }
128 }
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(untagged)]
140pub enum JsonRpcResponse<T> {
141 Success(JsonRpcSuccessResponse<T>),
143 Error(JsonRpcErrorResponse),
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct JsonRpcSuccessResponse<T> {
152 pub jsonrpc: JsonRpcVersion,
154
155 pub id: JsonRpcId,
157
158 pub result: T,
160}
161
162impl<T> JsonRpcSuccessResponse<T> {
163 #[must_use]
165 pub const fn new(id: JsonRpcId, result: T) -> Self {
166 Self {
167 jsonrpc: JsonRpcVersion,
168 id,
169 result,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct JsonRpcErrorResponse {
179 pub jsonrpc: JsonRpcVersion,
181
182 pub id: JsonRpcId,
185
186 pub error: JsonRpcError,
188}
189
190impl JsonRpcErrorResponse {
191 #[must_use]
193 pub const fn new(id: JsonRpcId, error: JsonRpcError) -> Self {
194 Self {
195 jsonrpc: JsonRpcVersion,
196 id,
197 error,
198 }
199 }
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct JsonRpcError {
207 pub code: i32,
209
210 pub message: String,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub data: Option<serde_json::Value>,
216}
217
218impl JsonRpcError {
219 #[must_use]
221 pub fn new(code: i32, message: impl Into<String>) -> Self {
222 Self {
223 code,
224 message: message.into(),
225 data: None,
226 }
227 }
228
229 #[must_use]
231 pub fn with_data(code: i32, message: impl Into<String>, data: serde_json::Value) -> Self {
232 Self {
233 code,
234 message: message.into(),
235 data: Some(data),
236 }
237 }
238}
239
240impl fmt::Display for JsonRpcError {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(f, "[{}] {}", self.code, self.message)
243 }
244}
245
246impl std::error::Error for JsonRpcError {}
247
248#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn version_serializes_as_2_0() {
256 let v = JsonRpcVersion;
257 let s = serde_json::to_string(&v).expect("serialize");
258 assert_eq!(s, "\"2.0\"");
259 }
260
261 #[test]
262 fn version_rejects_wrong_version() {
263 let result: Result<JsonRpcVersion, _> = serde_json::from_str("\"1.0\"");
264 assert!(result.is_err(), "should reject non-2.0 version");
265 }
266
267 #[test]
268 fn version_accepts_2_0() {
269 let v: JsonRpcVersion = serde_json::from_str("\"2.0\"").expect("deserialize");
270 assert_eq!(v, JsonRpcVersion);
271 }
272
273 #[test]
274 fn request_roundtrip() {
275 let req = JsonRpcRequest::with_params(
276 serde_json::json!(1),
277 "message/send",
278 serde_json::json!({"message": {}}),
279 );
280 let json = serde_json::to_string(&req).expect("serialize");
281 assert!(json.contains("\"jsonrpc\":\"2.0\""));
282 assert!(json.contains("\"method\":\"message/send\""));
283
284 let back: JsonRpcRequest = serde_json::from_str(&json).expect("deserialize");
285 assert_eq!(back.method, "message/send");
286 }
287
288 #[test]
289 fn success_response_roundtrip() {
290 let resp: JsonRpcResponse<serde_json::Value> =
291 JsonRpcResponse::Success(JsonRpcSuccessResponse::new(
292 Some(serde_json::json!(42)),
293 serde_json::json!({"status": "ok"}),
294 ));
295 let json = serde_json::to_string(&resp).expect("serialize");
296 assert!(json.contains("\"result\""));
297 assert!(!json.contains("\"error\""));
298 }
299
300 #[test]
301 fn error_response_roundtrip() {
302 let resp: JsonRpcResponse<serde_json::Value> =
303 JsonRpcResponse::Error(JsonRpcErrorResponse::new(
304 Some(serde_json::json!(1)),
305 JsonRpcError::new(-32601, "Method not found"),
306 ));
307 let json = serde_json::to_string(&resp).expect("serialize");
308 assert!(json.contains("\"error\""));
309 assert!(json.contains("-32601"));
310 }
311
312 #[test]
313 fn notification_has_no_id() {
314 let n = JsonRpcRequest::notification("task/cancel", None);
315 let json = serde_json::to_string(&n).expect("serialize");
316 assert!(
317 !json.contains("\"id\""),
318 "notification must omit id: {json}"
319 );
320 }
321}