nimiq_jsonrpc_core/
lib.rs

1use std::{
2    convert::TryFrom,
3    fmt::{Display, Formatter},
4    ops::RangeInclusive,
5    str::FromStr,
6};
7
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use thiserror::Error;
11
12mod sensitive;
13
14pub use self::sensitive::Sensitive;
15
16pub const JSONRPC_VERSION: &str = "2.0";
17pub const JSONRPC_RESERVED_ERROR_CODES: RangeInclusive<i64> = -32768..=-32000;
18
19/// An error of this JSON-RPC implementation. This can be either an error object returned by the server, or
20/// any other error that might be triggered in the server or client (e.g. a network error).
21#[derive(Debug, Error)]
22pub enum Error {
23    /// An error object sent by the server.
24    #[error("The server responded with an error: {0}")]
25    JsonRpc(#[from] RpcError),
26
27    /// JSON parsing error.
28    #[error("JSON error: {0}")]
29    Json(#[from] serde_json::Error),
30
31    #[error("Received invalid response")]
32    InvalidResponse,
33
34    #[error("Invalid subscription ID: {0:?}")]
35    InvalidSubscriptionId(Value),
36}
37
38/// Indicate if a websocket frame response should be in Binary or Text
39#[derive(Copy, Clone, Default)]
40pub enum FrameType {
41    /// Binary frame type
42    #[default]
43    Binary,
44    /// Text frame type
45    Text,
46}
47
48impl From<&String> for FrameType {
49    fn from(value: &String) -> Self {
50        match value.as_str() {
51            "text" => FrameType::Text,
52            "binary" => FrameType::Binary,
53            _ => FrameType::Binary,
54        }
55    }
56}
57
58/// A JSON-RPC request or response can either be a single request or response, or a list of the former. This `enum`
59/// matches either for serialization and deserialization.
60///
61/// [1] https://www.jsonrpc.org/specification#batch
62///
63#[derive(Clone, Debug, Serialize, Deserialize)]
64#[serde(untagged)]
65pub enum SingleOrBatch<T> {
66    Single(T),
67    Batch(Vec<T>),
68}
69
70/// Enum that is either a request or response object.
71#[derive(Clone, Debug, Deserialize)]
72#[serde(untagged)]
73pub enum RequestOrResponse {
74    Request(Request),
75    Response(Response),
76}
77
78impl RequestOrResponse {
79    pub fn from_slice(d: &[u8]) -> Result<Self, Error> {
80        Ok(serde_json::from_slice(d)?)
81    }
82
83    pub fn from_value(v: Value) -> Result<Self, Error> {
84        Ok(serde_json::from_value(v)?)
85    }
86}
87
88impl FromStr for RequestOrResponse {
89    type Err = Error;
90
91    fn from_str(s: &str) -> Result<Self, Self::Err> {
92        Ok(serde_json::from_str(s)?)
93    }
94}
95
96/// A single JSON-RPC request object
97///
98/// [1] https://www.jsonrpc.org/specification#request_object
99///
100#[derive(Clone, Debug, Serialize, Deserialize)]
101#[serde(deny_unknown_fields)]
102pub struct Request {
103    /// The version of the protocol. Must be `"2.0"`. See [[`JSONRPC_VERSION`]].
104    pub jsonrpc: String,
105
106    /// Name of the method to be called.
107    pub method: String,
108
109    /// The parameters for the function call.
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub params: Option<Value>,
112
113    /// Identifier sent by client to match to responses. If send by the client, the server will include this, in their
114    /// responses.
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub id: Option<Value>,
117}
118
119impl Request {
120    /// Creates a new request object.
121    ///
122    pub fn new(method: String, params: Option<Value>, id: Option<Value>) -> Self {
123        Self {
124            jsonrpc: JSONRPC_VERSION.to_owned(),
125            method,
126            params,
127            id,
128        }
129    }
130
131    pub fn build<P, I>(
132        method: String,
133        params_opt: Option<&P>,
134        id_opt: Option<&I>,
135    ) -> Result<Self, Error>
136    where
137        P: Serialize,
138        I: Serialize,
139    {
140        let params = params_opt.map(serde_json::to_value).transpose()?;
141        let id = id_opt.map(serde_json::to_value).transpose()?;
142
143        Ok(Self::new(method, params, id))
144    }
145
146    /// Verifies the correctness of the request and either returns nothing if the request was correct, or with a
147    /// `RpcError` object with the appropriate error to reply with.
148    pub fn verify(&self) -> Result<(), RpcError> {
149        if self.jsonrpc != JSONRPC_VERSION {
150            return Err(RpcError::invalid_request(Some(format!(
151                "Field 'jsonrpc' must be '2.0', but was '{}'",
152                self.jsonrpc
153            ))));
154        }
155
156        match &self.id {
157            Some(Value::String(_)) => {} // Ok
158            Some(Value::Number(n)) => {
159                if n.is_f64() {
160                    return Err(RpcError::invalid_request(Some(format!(
161                        "Field 'id' is a number, but should not be fractional: {:?}",
162                        self.id
163                    ))));
164                }
165                // otherwise numbers are ok.
166            }
167            _ => {
168                return Err(RpcError::invalid_request(Some(format!(
169                    "Invalid type in field 'id': {:?}",
170                    self.id
171                ))))
172            }
173        }
174
175        Ok(())
176    }
177
178    pub fn from_slice(d: &[u8]) -> Result<Self, Error> {
179        Ok(serde_json::from_slice(d)?)
180    }
181
182    pub fn to_value(&self) -> Result<Value, Error> {
183        Ok(serde_json::to_value(self)?)
184    }
185}
186
187/// A single JSON-RPC response object.
188///
189/// [1] https://www.jsonrpc.org/specification#response_object
190///
191#[derive(Clone, Debug, Serialize, Deserialize)]
192#[serde(deny_unknown_fields)]
193pub struct Response {
194    /// The version of the protocol. Must be `"2.0"`. See [[`JSONRPC_VERSION`]].
195    pub jsonrpc: String,
196
197    /// The result of the function call that is returned to the client. This is `None`, if there was an error. This
198    /// might also be `None`, if the call at the server returned `null`.
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub result: Option<Value>,
201
202    /// The error triggered by a request, if present.
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub error: Option<RpcError>,
205
206    /// The ID to identify the to which request this response belongs. If the client sent an ID in the request, this
207    /// will be replied here. This is `null`, if the response is an error that was triggered when trying to parse the
208    /// request ID
209    pub id: Value,
210}
211
212impl Response {
213    /// Creates a new successful response.
214    pub fn new_success(id: Value, result: Value) -> Self {
215        Self {
216            jsonrpc: JSONRPC_VERSION.to_owned(),
217            result: Some(result),
218            error: None,
219            id,
220        }
221    }
222
223    /// Creates a new error response.
224    pub fn new_error(id: Value, error: RpcError) -> Self {
225        Self {
226            jsonrpc: JSONRPC_VERSION.to_owned(),
227            result: None,
228            error: Some(error),
229            id,
230        }
231    }
232
233    /// Creates a response object from a [[std::result::Result]].
234    pub fn from_result<R>(id: Value, result: Result<R, RpcError>) -> Result<Self, Error>
235    where
236        R: Serialize,
237    {
238        Ok(match result {
239            Ok(result) => Self::new_success(id, serde_json::to_value(&result)?),
240            Err(e) => Self::new_error(id, e),
241        })
242    }
243
244    /// Converts the response object to a [[std::result::Result]]
245    pub fn into_result<R>(self) -> Result<R, Error>
246    where
247        R: for<'de> Deserialize<'de>,
248    {
249        match (self.result, self.error) {
250            (Some(result), None) => Ok(serde_json::from_value(result)?),
251            (None, Some(error)) => Err(error.into()),
252            _ => Err(Error::InvalidResponse),
253        }
254    }
255
256    pub fn from_slice(d: &[u8]) -> Result<Self, Error> {
257        Ok(serde_json::from_slice(d)?)
258    }
259}
260
261/// Numeric error code used in error objects.
262pub type ErrorCode = i64;
263
264/// An error object that can be returned by the server.
265///
266/// [1] https://www.jsonrpc.org/specification#error_object
267///
268#[derive(Clone, Debug, Error, Serialize, Deserialize)]
269#[serde(deny_unknown_fields)]
270pub struct RpcError {
271    /// The error code as defined by the JSON-RPC specification.
272    pub code: i64,
273
274    /// An optional error message.
275    pub message: Option<String>,
276
277    /// Optional data attached to the error object by the server.
278    #[serde(skip_serializing_if = "Option::is_none")]
279    pub data: Option<Value>,
280}
281
282impl std::fmt::Display for RpcError {
283    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
284        write!(f, "JSON-RPC error: code={}", self.code)?;
285        if let Some(message) = &self.message {
286            write!(f, ": {}", message)?;
287        }
288        if let Some(data) = &self.data {
289            write!(f, " - Caused by:  {}", data)?;
290        }
291        Ok(())
292    }
293}
294
295impl RpcError {
296    fn new_reserved(code: i64, message: &'static str, data: Option<Value>) -> Self {
297        Self {
298            code,
299            message: Some(message.to_owned()),
300            data,
301        }
302    }
303
304    fn new_reserved_with_description(
305        code: i64,
306        message: &'static str,
307        description: Option<String>,
308    ) -> Self {
309        Self {
310            code,
311            message: Some(message.to_owned()),
312            data: description.map(Value::from),
313        }
314    }
315
316    pub fn parse_error(description: Option<String>) -> Self {
317        Self::new_reserved_with_description(-32700, "Parse error", description)
318    }
319
320    pub fn invalid_request(description: Option<String>) -> Self {
321        Self::new_reserved_with_description(-32600, "Invalid Request", description)
322    }
323
324    pub fn method_not_found(description: Option<String>) -> Self {
325        Self::new_reserved_with_description(-32601, "Method not found", description)
326    }
327
328    pub fn invalid_params(description: Option<String>) -> Self {
329        Self::new_reserved_with_description(-32602, "Invalid params", description)
330    }
331
332    pub fn internal_from_string(description: Option<String>) -> Self {
333        Self::new_reserved_with_description(-32603, "Internal error", description)
334    }
335
336    pub fn internal_error(data: Option<Value>) -> Self {
337        Self::new_reserved(-32603, "Internal error", data)
338    }
339}
340
341impl Default for RpcError {
342    fn default() -> Self {
343        Self::internal_error(None)
344    }
345}
346
347impl From<()> for RpcError {
348    fn from(_: ()) -> Self {
349        Self::default()
350    }
351}
352
353#[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq)]
354#[serde(untagged)]
355pub enum SubscriptionId {
356    String(String),
357    Number(u64),
358}
359
360impl TryFrom<Value> for SubscriptionId {
361    type Error = Error;
362
363    fn try_from(value: Value) -> Result<Self, Error> {
364        match value {
365            Value::String(s) => Ok(SubscriptionId::String(s)),
366            Value::Number(n) => n
367                .as_u64()
368                .map(SubscriptionId::Number)
369                .ok_or(Error::InvalidSubscriptionId(Value::Number(n))),
370            value => Err(Error::InvalidSubscriptionId(value)),
371        }
372    }
373}
374
375impl From<u64> for SubscriptionId {
376    fn from(n: u64) -> Self {
377        SubscriptionId::Number(n)
378    }
379}
380
381impl From<String> for SubscriptionId {
382    fn from(s: String) -> Self {
383        SubscriptionId::String(s)
384    }
385}
386
387impl Display for SubscriptionId {
388    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
389        match self {
390            SubscriptionId::String(s) => write!(f, "{}", s),
391            SubscriptionId::Number(n) => write!(f, "{}", n),
392        }
393    }
394}
395
396#[derive(Clone, Debug, Serialize, Deserialize)]
397pub struct SubscriptionMessage<T> {
398    pub subscription: SubscriptionId,
399    pub result: T,
400}