Skip to main content

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}