jsonrpc_lite/jsonrpc.rs
1//! JSON-RPC 2.0 implementation
2//!
3//! This module provides types and utilities for working with JSON-RPC 2.0 protocol.
4//! It implements the core JSON-RPC 2.0 objects as defined in the specification,
5//! including requests, notifications, and responses.
6//!
7//! The main type is `JsonRpc` which represents all possible JSON-RPC message types.
8//! Helper methods are provided for creating and parsing JSON-RPC messages.
9
10use serde::{Deserialize, Serialize};
11use serde_json::{Map, Result as SerdeResult, Value};
12
13use crate::Error as RpcError;
14
15/// An identifier established by the Client that MUST contain a String, Number,
16/// or NULL value if included. If it is not included it is assumed to be a notification.
17/// The value SHOULD normally not be Null and Numbers SHOULD NOT contain fractional parts
18///
19/// As per the JSON-RPC 2.0 specification, this identifier is used to correlate
20/// requests with their corresponding responses.
21#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash)]
22#[serde(untagged)]
23pub enum Id {
24 /// Numeric identifier
25 Num(i64),
26 /// String identifier
27 Str(String),
28 /// Null identifier (represented as unit type in Rust)
29 None(()),
30}
31
32impl From<()> for Id {
33 /// Converts unit type to Id::None
34 ///
35 /// # Arguments
36 ///
37 /// * `val` - The unit value to convert
38 ///
39 /// # Returns
40 ///
41 /// A new Id::None variant
42 fn from(val: ()) -> Self {
43 Id::None(val)
44 }
45}
46
47impl From<i64> for Id {
48 /// Converts an i64 to Id::Num
49 ///
50 /// # Arguments
51 ///
52 /// * `val` - The i64 value to convert
53 ///
54 /// # Returns
55 ///
56 /// A new Id::Num variant containing the provided value
57 fn from(val: i64) -> Self {
58 Id::Num(val)
59 }
60}
61
62impl From<String> for Id {
63 /// Converts a String to Id::Str
64 ///
65 /// # Arguments
66 ///
67 /// * `val` - The String value to convert
68 ///
69 /// # Returns
70 ///
71 /// A new Id::Str variant containing the provided value
72 fn from(val: String) -> Self {
73 Id::Str(val)
74 }
75}
76
77/// A Structured value that holds the parameter values
78/// to be used during the invocation of the method.
79/// This member MAY be omitted.
80///
81/// Parameters can be provided as either an ordered array or a named map,
82/// as per the JSON-RPC 2.0 specification.
83#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
84#[serde(untagged)]
85pub enum Params {
86 /// Parameters as an ordered array of values
87 Array(Vec<Value>),
88 /// Parameters as a map of named values
89 Map(Map<String, Value>),
90 /// No parameters (represented as unit type in Rust)
91 None(()),
92}
93
94impl From<Value> for Params {
95 /// Converts a serde_json::Value to Params
96 ///
97 /// # Arguments
98 ///
99 /// * `val` - The Value to convert
100 ///
101 /// # Returns
102 ///
103 /// - Params::Array if the value is an array
104 /// - Params::Map if the value is an object
105 /// - Params::None otherwise
106 fn from(val: Value) -> Self {
107 match val {
108 Value::Array(v) => Params::Array(v),
109 Value::Object(v) => Params::Map(v),
110 _ => Params::None(()),
111 }
112 }
113}
114
115impl From<Vec<Value>> for Params {
116 /// Converts a Vec<Value> to Params::Array
117 ///
118 /// # Arguments
119 ///
120 /// * `val` - The vector to convert
121 ///
122 /// # Returns
123 ///
124 /// A new Params::Array variant containing the provided vector
125 fn from(val: Vec<Value>) -> Self {
126 Params::Array(val)
127 }
128}
129
130impl From<Map<String, Value>> for Params {
131 /// Converts a Map<String, Value> to Params::Map
132 ///
133 /// # Arguments
134 ///
135 /// * `val` - The map to convert
136 ///
137 /// # Returns
138 ///
139 /// A new Params::Map variant containing the provided map
140 fn from(val: Map<String, Value>) -> Self {
141 Params::Map(val)
142 }
143}
144
145impl From<Params> for Value {
146 /// Converts a Params to a serde_json Value
147 ///
148 /// # Arguments
149 ///
150 /// * `val` - The Params to convert
151 ///
152 /// # Returns
153 ///
154 /// A new serde_json Value
155 fn from(val: Params) -> Self {
156 match val {
157 Params::Array(values) => values.into(),
158 Params::Map(map) => map.into(),
159 Params::None(()) => Value::Null,
160 }
161 }
162}
163
164/// JSON-RPC 2.0 Request object
165///
166/// A request object represents a call to a method on the server.
167/// It contains a method name, optional parameters, and an identifier
168/// that will be used to match the response to this request.
169#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
170pub struct Request {
171 /// JSON-RPC protocol version (always "2.0")
172 jsonrpc: String,
173 /// Name of the method to be invoked
174 pub method: String,
175 /// Parameters to be used during the invocation of the method
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub params: Option<Params>,
178 /// Client-established identifier for this request
179 pub id: Id,
180}
181
182impl Request {
183 /// Gets the JSON-RPC protocol version
184 ///
185 /// # Returns
186 ///
187 /// The protocol version string ("2.0") or None if not available
188 pub fn get_version(&self) -> &str {
189 &self.jsonrpc
190 }
191}
192
193/// JSON-RPC 2.0 Notification object
194///
195/// A notification is similar to a request but does not require a response.
196/// It contains a method name and optional parameters, but no identifier.
197#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
198pub struct Notification {
199 /// JSON-RPC protocol version (always "2.0")
200 jsonrpc: String,
201 /// Name of the method to be invoked
202 pub method: String,
203 /// Parameters to be used during the invocation of the method
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub params: Option<Params>,
206}
207
208impl Notification {
209 /// Gets the JSON-RPC protocol version
210 ///
211 /// # Returns
212 ///
213 /// The protocol version string ("2.0") or None if not available
214 pub fn get_version(&self) -> &str {
215 &self.jsonrpc
216 }
217}
218
219/// JSON-RPC 2.0 Success Response object
220///
221/// A success response is sent when a request has been processed successfully.
222/// It contains the result of the method call and the identifier from the request.
223#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
224pub struct Success {
225 /// JSON-RPC protocol version (always "2.0")
226 jsonrpc: String,
227 /// The result of the method call
228 pub result: Value,
229 /// Client-established identifier matching the request
230 pub id: Id,
231}
232
233impl Success {
234 /// Gets the JSON-RPC protocol version
235 ///
236 /// # Returns
237 ///
238 /// The protocol version string ("2.0") or None if not available
239 pub fn get_version(&self) -> &str {
240 &self.jsonrpc
241 }
242}
243
244/// JSON-RPC 2.0 Error Response object
245///
246/// An error response is sent when a request could not be processed successfully.
247/// It contains an error object and the identifier from the request.
248#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
249pub struct Error {
250 /// JSON-RPC protocol version (always "2.0")
251 jsonrpc: String,
252 /// The error that occurred
253 pub error: RpcError,
254 /// Client-established identifier matching the request
255 pub id: Id,
256}
257
258impl Error {
259 /// Gets the JSON-RPC protocol version
260 ///
261 /// # Returns
262 ///
263 /// The protocol version string ("2.0") or None if not available
264 pub fn get_version(&self) -> &str {
265 &self.jsonrpc
266 }
267}
268
269/// JSON-RPC 2.0 Request object and Response object
270/// [JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification).
271///
272/// This enum represents all possible JSON-RPC message types:
273/// - Request: A method call with an identifier
274/// - Notification: A method call without an identifier (no response expected)
275/// - Success: A successful response to a request
276/// - Error: An error response to a request
277#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
278#[serde(untagged)]
279pub enum JsonRpc {
280 /// Request object
281 Request(Request),
282 /// Notification object
283 Notification(Notification),
284 /// Success Response
285 Success(Success),
286 /// Error Response
287 Error(Error),
288}
289
290impl JsonRpc {
291 /// Creates a JSON-RPC 2.0 request object without params
292 ///
293 /// # Arguments
294 ///
295 /// * `id` - The identifier for the request
296 /// * `method` - The name of the method to call
297 ///
298 /// # Returns
299 ///
300 /// A new JsonRpc::Request variant
301 ///
302 /// # Examples
303 ///
304 /// ```
305 /// use jsonrpc_lite::JsonRpc;
306 ///
307 /// let request = JsonRpc::request(1, "echo");
308 /// ```
309 pub fn request<I: Into<Id>>(id: I, method: &str) -> Self {
310 JsonRpc::Request(Request {
311 jsonrpc: String::from("2.0"),
312 method: String::from(method),
313 params: None,
314 id: id.into(),
315 })
316 }
317
318 /// Creates a JSON-RPC 2.0 request object with params
319 ///
320 /// # Arguments
321 ///
322 /// * `id` - The identifier for the request
323 /// * `method` - The name of the method to call
324 /// * `params` - The parameters to pass to the method
325 ///
326 /// # Returns
327 ///
328 /// A new JsonRpc::Request variant with parameters
329 ///
330 /// # Examples
331 ///
332 /// ```
333 /// use jsonrpc_lite::JsonRpc;
334 /// use serde_json::json;
335 ///
336 /// let request = JsonRpc::request_with_params(1, "add", json!([1, 2]));
337 /// ```
338 pub fn request_with_params<I: Into<Id>, P: Into<Params>>(
339 id: I,
340 method: &str,
341 params: P,
342 ) -> Self {
343 JsonRpc::Request(Request {
344 jsonrpc: String::from("2.0"),
345 method: String::from(method),
346 params: Some(params.into()),
347 id: id.into(),
348 })
349 }
350
351 /// Creates a JSON-RPC 2.0 notification object without params
352 ///
353 /// # Arguments
354 ///
355 /// * `method` - The name of the method to call
356 ///
357 /// # Returns
358 ///
359 /// A new JsonRpc::Notification variant
360 ///
361 /// # Examples
362 ///
363 /// ```
364 /// use jsonrpc_lite::JsonRpc;
365 ///
366 /// let notification = JsonRpc::notification("ping");
367 /// ```
368 pub fn notification(method: &str) -> Self {
369 JsonRpc::Notification(Notification {
370 jsonrpc: String::from("2.0"),
371 method: String::from(method),
372 params: None,
373 })
374 }
375
376 /// Creates a JSON-RPC 2.0 notification object with params
377 ///
378 /// # Arguments
379 ///
380 /// * `method` - The name of the method to call
381 /// * `params` - The parameters to pass to the method
382 ///
383 /// # Returns
384 ///
385 /// A new JsonRpc::Notification variant with parameters
386 ///
387 /// # Examples
388 ///
389 /// ```
390 /// use jsonrpc_lite::JsonRpc;
391 /// use serde_json::json;
392 ///
393 /// let notification = JsonRpc::notification_with_params("log", json!({"level": "info", "message": "Hello"}));
394 /// ```
395 pub fn notification_with_params<P: Into<Params>>(method: &str, params: P) -> Self {
396 JsonRpc::Notification(Notification {
397 jsonrpc: String::from("2.0"),
398 method: String::from(method),
399 params: Some(params.into()),
400 })
401 }
402
403 /// Creates a JSON-RPC 2.0 success response object
404 ///
405 /// # Arguments
406 ///
407 /// * `id` - The identifier matching the request
408 /// * `result` - The result of the method call
409 ///
410 /// # Returns
411 ///
412 /// A new JsonRpc::Success variant
413 ///
414 /// # Examples
415 ///
416 /// ```
417 /// use jsonrpc_lite::JsonRpc;
418 /// use serde_json::json;
419 ///
420 /// let response = JsonRpc::success(1, &json!(42));
421 /// ```
422 pub fn success<I: Into<Id>>(id: I, result: &Value) -> Self {
423 JsonRpc::Success(Success {
424 jsonrpc: String::from("2.0"),
425 result: result.clone(),
426 id: id.into(),
427 })
428 }
429
430 /// Creates a JSON-RPC 2.0 error response object
431 ///
432 /// # Arguments
433 ///
434 /// * `id` - The identifier matching the request
435 /// * `error` - The error that occurred
436 ///
437 /// # Returns
438 ///
439 /// A new JsonRpc::Error variant
440 ///
441 /// # Examples
442 ///
443 /// ```
444 /// use jsonrpc_lite::{JsonRpc, Error};
445 ///
446 /// let response = JsonRpc::error(1, Error::method_not_found());
447 /// ```
448 pub fn error<I: Into<Id>>(id: I, error: RpcError) -> Self {
449 JsonRpc::Error(Error {
450 jsonrpc: String::from("2.0"),
451 error,
452 id: id.into(),
453 })
454 }
455
456 /// Gets the JSON-RPC protocol version
457 ///
458 /// # Returns
459 ///
460 /// The protocol version string ("2.0") or None if not available
461 pub fn get_version(&self) -> Option<&str> {
462 match self {
463 JsonRpc::Notification(ref v) => Some(&v.jsonrpc),
464 JsonRpc::Request(ref v) => Some(&v.jsonrpc),
465 JsonRpc::Success(ref v) => Some(&v.jsonrpc),
466 JsonRpc::Error(ref v) => Some(&v.jsonrpc),
467 }
468 }
469
470 /// Gets the identifier from the JSON-RPC message
471 ///
472 /// # Returns
473 ///
474 /// The identifier if present (for requests and responses), or None for notifications
475 pub fn get_id(&self) -> Option<Id> {
476 match *self {
477 JsonRpc::Request(ref v) => Some(v.id.clone()),
478 JsonRpc::Success(ref v) => Some(v.id.clone()),
479 JsonRpc::Error(ref v) => Some(v.id.clone()),
480 _ => None,
481 }
482 }
483
484 /// Gets the method name from the JSON-RPC message
485 ///
486 /// # Returns
487 ///
488 /// The method name if present (for requests and notifications), or None for responses
489 pub fn get_method(&self) -> Option<&str> {
490 match *self {
491 JsonRpc::Notification(ref v) => Some(&v.method),
492 JsonRpc::Request(ref v) => Some(&v.method),
493 _ => None,
494 }
495 }
496
497 /// Gets the parameters from the JSON-RPC message
498 ///
499 /// # Returns
500 ///
501 /// The parameters if present (for requests and notifications), or None for responses
502 pub fn get_params(&self) -> Option<Params> {
503 match *self {
504 JsonRpc::Notification(ref v) => v.params.as_ref().cloned(),
505 JsonRpc::Request(ref v) => v.params.as_ref().cloned(),
506 _ => None,
507 }
508 }
509
510 /// Gets the result from a successful JSON-RPC response
511 ///
512 /// # Returns
513 ///
514 /// The result if this is a success response, or None otherwise
515 pub fn get_result(&self) -> Option<&Value> {
516 match *self {
517 JsonRpc::Success(ref v) => Some(&v.result),
518 _ => None,
519 }
520 }
521
522 /// Gets the error from an error JSON-RPC response
523 ///
524 /// # Returns
525 ///
526 /// The error if this is an error response, or None otherwise
527 pub fn get_error(&self) -> Option<&RpcError> {
528 match *self {
529 JsonRpc::Error(ref v) => Some(&v.error),
530 _ => None,
531 }
532 }
533
534 /// Parses a JSON string into a JSON-RPC message
535 ///
536 /// # Arguments
537 ///
538 /// * `input` - The JSON string to parse
539 ///
540 /// # Returns
541 ///
542 /// A Result containing either the parsed JsonRpc or a serde_json error
543 ///
544 /// # Examples
545 ///
546 /// ```
547 /// use jsonrpc_lite::JsonRpc;
548 ///
549 /// let input = r#"{"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":1}"#;
550 /// let request = JsonRpc::parse(input).unwrap();
551 /// ```
552 pub fn parse(input: &str) -> SerdeResult<Self> {
553 use serde_json::from_str;
554 from_str(input)
555 }
556
557 /// Parses a JSON string into a vector of JSON-RPC messages
558 ///
559 /// This is useful for batch requests and responses.
560 ///
561 /// # Arguments
562 ///
563 /// * `input` - The JSON string to parse
564 ///
565 /// # Returns
566 ///
567 /// A Result containing either a vector of parsed JsonRpc objects or a serde_json error
568 ///
569 /// # Examples
570 ///
571 /// ```
572 /// use jsonrpc_lite::JsonRpc;
573 ///
574 /// let input = r#"[{"jsonrpc":"2.0","method":"sum","params":[1,2,4],"id":"1"},{"jsonrpc":"2.0","method":"notify_hello","params":[7]}]"#;
575 /// let batch = JsonRpc::parse_vec(input).unwrap();
576 /// assert_eq!(batch.len(), 2);
577 /// ```
578 pub fn parse_vec(input: &str) -> SerdeResult<Vec<Self>> {
579 use serde_json::from_str;
580 from_str(input)
581 }
582}
583
584#[cfg(test)]
585mod tests {
586 use super::*;
587 use serde_json::{json, to_value};
588
589 #[test]
590 fn request() {
591 let jsonrpc = to_value(JsonRpc::request((), "test"))
592 .expect("Unable to turn request into a Json Value");
593 assert_eq!(
594 jsonrpc,
595 json!({
596 "id": null,
597 "jsonrpc": "2.0",
598 "method": "test"
599 })
600 );
601 }
602
603 #[test]
604 fn request_with_params_vec() {
605 let jsonrpc = to_value(JsonRpc::request_with_params(
606 46714,
607 "test",
608 json!([true, false, false, true]),
609 ))
610 .expect("Unable to turn request_with_params_vec into a Json Value");
611 assert_eq!(
612 jsonrpc,
613 json!({
614 "id": 46714,
615 "jsonrpc": "2.0",
616 "method": "test",
617 "params": [true, false, false, true]
618 })
619 );
620 }
621
622 #[test]
623 fn request_with_params_map() {
624 let jsonrpc = to_value(JsonRpc::request_with_params(
625 String::from("alpha-gamma-06714"),
626 "test",
627 json!({
628 "key": "94151351-5651651658-56151351351",
629 "n": 5158,
630 "mean": 454.54
631 }),
632 ))
633 .expect("Unable to turn request_with_params_map into a Json Value");
634 assert_eq!(
635 jsonrpc,
636 json!({
637 "id": "alpha-gamma-06714",
638 "jsonrpc": "2.0",
639 "method": "test",
640 "params": {
641 "key": "94151351-5651651658-56151351351",
642 "n": 5158,
643 "mean": 454.54
644 }
645 })
646 );
647 }
648}