1use crate::error::JsonRpcError;
29use serde::{Deserialize, Serialize};
30use std::borrow::Cow;
31
32pub const JSONRPC_VERSION: &str = "2.0";
34
35#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40#[serde(untagged)]
41pub enum RequestId {
42 Number(u64),
44 String(String),
46 Null,
51}
52
53impl RequestId {
54 #[must_use]
56 pub const fn number(id: u64) -> Self {
57 Self::Number(id)
58 }
59
60 #[must_use]
62 pub fn string(id: impl Into<String>) -> Self {
63 Self::String(id.into())
64 }
65}
66
67impl From<u64> for RequestId {
68 fn from(id: u64) -> Self {
69 Self::Number(id)
70 }
71}
72
73impl From<String> for RequestId {
74 fn from(id: String) -> Self {
75 Self::String(id)
76 }
77}
78
79impl From<&str> for RequestId {
80 fn from(id: &str) -> Self {
81 Self::String(id.to_string())
82 }
83}
84
85impl std::fmt::Display for RequestId {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 Self::Number(n) => write!(f, "{n}"),
89 Self::String(s) => write!(f, "{s}"),
90 Self::Null => write!(f, "null"),
91 }
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct Request {
101 pub jsonrpc: Cow<'static, str>,
103 pub id: RequestId,
105 pub method: Cow<'static, str>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub params: Option<serde_json::Value>,
110}
111
112impl Request {
113 #[must_use]
115 pub fn new(method: impl Into<Cow<'static, str>>, id: impl Into<RequestId>) -> Self {
116 Self {
117 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
118 id: id.into(),
119 method: method.into(),
120 params: None,
121 }
122 }
123
124 #[must_use]
126 pub fn with_params(
127 method: impl Into<Cow<'static, str>>,
128 id: impl Into<RequestId>,
129 params: serde_json::Value,
130 ) -> Self {
131 Self {
132 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
133 id: id.into(),
134 method: method.into(),
135 params: Some(params),
136 }
137 }
138
139 #[must_use]
141 pub fn params(mut self, params: serde_json::Value) -> Self {
142 self.params = Some(params);
143 self
144 }
145
146 #[must_use]
148 pub fn method(&self) -> &str {
149 &self.method
150 }
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct Response {
159 pub jsonrpc: Cow<'static, str>,
161 pub id: RequestId,
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub result: Option<serde_json::Value>,
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub error: Option<JsonRpcError>,
169}
170
171impl Response {
172 #[must_use]
174 pub fn success(id: impl Into<RequestId>, result: serde_json::Value) -> Self {
175 Self {
176 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
177 id: id.into(),
178 result: Some(result),
179 error: None,
180 }
181 }
182
183 #[must_use]
185 pub fn error(id: impl Into<RequestId>, error: JsonRpcError) -> Self {
186 Self {
187 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
188 id: id.into(),
189 result: None,
190 error: Some(error),
191 }
192 }
193
194 #[must_use]
196 pub const fn is_success(&self) -> bool {
197 self.result.is_some() && self.error.is_none()
198 }
199
200 #[must_use]
202 pub const fn is_error(&self) -> bool {
203 self.error.is_some()
204 }
205
206 pub fn into_result(self) -> Result<serde_json::Value, JsonRpcError> {
210 if let Some(error) = self.error {
211 Err(error)
212 } else {
213 self.result.ok_or_else(|| JsonRpcError {
214 code: -32603,
215 message: "Response contained neither result nor error".to_string(),
216 data: None,
217 })
218 }
219 }
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct Notification {
228 pub jsonrpc: Cow<'static, str>,
230 pub method: Cow<'static, str>,
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub params: Option<serde_json::Value>,
235}
236
237impl Notification {
238 #[must_use]
240 pub fn new(method: impl Into<Cow<'static, str>>) -> Self {
241 Self {
242 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
243 method: method.into(),
244 params: None,
245 }
246 }
247
248 #[must_use]
250 pub fn with_params(method: impl Into<Cow<'static, str>>, params: serde_json::Value) -> Self {
251 Self {
252 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
253 method: method.into(),
254 params: Some(params),
255 }
256 }
257
258 #[must_use]
260 pub fn params(mut self, params: serde_json::Value) -> Self {
261 self.params = Some(params);
262 self
263 }
264
265 #[must_use]
267 pub fn method(&self) -> &str {
268 &self.method
269 }
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
277#[serde(untagged)]
278pub enum Message {
279 Request(Request),
281 Response(Response),
283 Notification(Notification),
285}
286
287impl Message {
288 #[must_use]
290 pub fn method(&self) -> Option<&str> {
291 match self {
292 Self::Request(r) => Some(&r.method),
293 Self::Notification(n) => Some(&n.method),
294 Self::Response(_) => None,
295 }
296 }
297
298 #[must_use]
300 pub const fn id(&self) -> Option<&RequestId> {
301 match self {
302 Self::Request(r) => Some(&r.id),
303 Self::Response(r) => Some(&r.id),
304 Self::Notification(_) => None,
305 }
306 }
307
308 #[must_use]
310 pub const fn is_request(&self) -> bool {
311 matches!(self, Self::Request(_))
312 }
313
314 #[must_use]
316 pub const fn is_response(&self) -> bool {
317 matches!(self, Self::Response(_))
318 }
319
320 #[must_use]
322 pub const fn is_notification(&self) -> bool {
323 matches!(self, Self::Notification(_))
324 }
325
326 #[must_use]
328 pub const fn as_request(&self) -> Option<&Request> {
329 match self {
330 Self::Request(r) => Some(r),
331 _ => None,
332 }
333 }
334
335 #[must_use]
337 pub const fn as_response(&self) -> Option<&Response> {
338 match self {
339 Self::Response(r) => Some(r),
340 _ => None,
341 }
342 }
343
344 #[must_use]
346 pub const fn as_notification(&self) -> Option<&Notification> {
347 match self {
348 Self::Notification(n) => Some(n),
349 _ => None,
350 }
351 }
352}
353
354impl From<Request> for Message {
355 fn from(r: Request) -> Self {
356 Self::Request(r)
357 }
358}
359
360impl From<Response> for Message {
361 fn from(r: Response) -> Self {
362 Self::Response(r)
363 }
364}
365
366impl From<Notification> for Message {
367 fn from(n: Notification) -> Self {
368 Self::Notification(n)
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
377#[serde(untagged)]
378pub enum ProgressToken {
379 Number(u64),
381 String(String),
383}
384
385impl std::fmt::Display for ProgressToken {
386 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387 match self {
388 Self::Number(n) => write!(f, "{n}"),
389 Self::String(s) => write!(f, "{s}"),
390 }
391 }
392}
393
394#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
399#[serde(transparent)]
400pub struct Cursor(pub String);
401
402impl Cursor {
403 #[must_use]
405 pub fn new(cursor: impl Into<String>) -> Self {
406 Self(cursor.into())
407 }
408}
409
410impl std::fmt::Display for Cursor {
411 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412 write!(f, "{}", self.0)
413 }
414}
415
416impl From<String> for Cursor {
417 fn from(s: String) -> Self {
418 Self(s)
419 }
420}
421
422impl From<&str> for Cursor {
423 fn from(s: &str) -> Self {
424 Self(s.to_string())
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431
432 #[test]
433 fn request_id_null_round_trips() {
434 assert_eq!(serde_json::to_string(&RequestId::Null).unwrap(), "null");
436 assert_eq!(
437 serde_json::from_str::<RequestId>("null").unwrap(),
438 RequestId::Null
439 );
440 assert_eq!(
442 serde_json::from_str::<RequestId>("7").unwrap(),
443 RequestId::Number(7)
444 );
445 assert_eq!(
446 serde_json::from_str::<RequestId>("\"abc\"").unwrap(),
447 RequestId::String("abc".to_string())
448 );
449 }
450
451 #[test]
452 fn test_request_serialization() -> Result<(), Box<dyn std::error::Error>> {
453 let request = Request::new("tools/list", 1u64);
454 let json = serde_json::to_string(&request)?;
455 assert!(json.contains("\"jsonrpc\":\"2.0\""));
456 assert!(json.contains("\"method\":\"tools/list\""));
457 assert!(json.contains("\"id\":1"));
458 Ok(())
459 }
460
461 #[test]
462 fn test_request_with_params() -> Result<(), Box<dyn std::error::Error>> {
463 let request = Request::with_params(
464 "tools/call",
465 1u64,
466 serde_json::json!({"name": "search", "arguments": {"query": "test"}}),
467 );
468 let json = serde_json::to_string(&request)?;
469 assert!(json.contains("\"params\""));
470 assert!(json.contains("\"name\":\"search\""));
471 Ok(())
472 }
473
474 #[test]
475 fn test_response_success() -> Result<(), Box<dyn std::error::Error>> {
476 let response = Response::success(1u64, serde_json::json!({"tools": []}));
477 assert!(response.is_success());
478 assert!(!response.is_error());
479
480 let result = response
481 .into_result()
482 .map_err(|e| format!("Error: {}", e.message))?;
483 assert!(result.get("tools").is_some());
484 Ok(())
485 }
486
487 #[test]
488 fn test_response_error() {
489 let error = JsonRpcError {
490 code: -32601,
491 message: "Method not found".to_string(),
492 data: None,
493 };
494 let response = Response::error(1u64, error);
495 assert!(!response.is_success());
496 assert!(response.is_error());
497
498 let err = response.into_result().unwrap_err();
500 assert_eq!(err.code, -32601);
501 }
502
503 #[test]
504 fn test_notification() -> Result<(), Box<dyn std::error::Error>> {
505 let notification = Notification::with_params(
506 "notifications/progress",
507 serde_json::json!({"progress": 50, "total": 100}),
508 );
509 let json = serde_json::to_string(¬ification)?;
510 assert!(json.contains("\"method\":\"notifications/progress\""));
511 assert!(!json.contains("\"id\"")); Ok(())
513 }
514
515 #[test]
516 fn test_message_parsing() -> Result<(), Box<dyn std::error::Error>> {
517 let json = r#"{"jsonrpc":"2.0","id":1,"method":"test"}"#;
519 let msg: Message = serde_json::from_str(json)?;
520 assert!(msg.is_request());
521 assert_eq!(msg.method(), Some("test"));
522
523 let json = r#"{"jsonrpc":"2.0","id":1,"result":{}}"#;
525 let msg: Message = serde_json::from_str(json)?;
526 assert!(msg.is_response());
527
528 let json = r#"{"jsonrpc":"2.0","method":"notify"}"#;
530 let msg: Message = serde_json::from_str(json)?;
531 assert!(msg.is_notification());
532 Ok(())
533 }
534
535 #[test]
536 fn test_request_id_types() -> Result<(), Box<dyn std::error::Error>> {
537 let request = Request::new("test", 42u64);
539 let json = serde_json::to_string(&request)?;
540 assert!(json.contains("\"id\":42"));
541
542 let request = Request::new("test", "req-001");
544 let json = serde_json::to_string(&request)?;
545 assert!(json.contains("\"id\":\"req-001\""));
546 Ok(())
547 }
548}