|
|
@@ -16,70 +16,104 @@
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
*/
|
|
|
|
|
|
-//! JSON-RPC 2.0 primitives
|
|
|
-use std::fmt;
|
|
|
+//! JSON-RPC 2.0 object definitions
|
|
|
+use std::collections::HashMap;
|
|
|
|
|
|
-use async_std::sync::Arc;
|
|
|
use darkfi_serial::{serialize, Encodable};
|
|
|
use rand::{rngs::OsRng, Rng};
|
|
|
-use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
-use serde_json::{json, Value};
|
|
|
+use tinyjson::JsonValue;
|
|
|
|
|
|
-use crate::system::{Subscriber, SubscriberPtr};
|
|
|
+use crate::{
|
|
|
+ error::RpcError,
|
|
|
+ system::{Subscriber, SubscriberPtr},
|
|
|
+ util::encoding::base64,
|
|
|
+ Result,
|
|
|
+};
|
|
|
|
|
|
/// JSON-RPC error codes.
|
|
|
-/// The error codes from and including -32768 to -32000 are reserved for pre-defined errors.
|
|
|
-#[derive(Debug, Clone)]
|
|
|
+/// The error codes `[-32768, -32000]` are reserved for predefined errors.
|
|
|
+#[derive(Copy, Clone, Debug)]
|
|
|
pub enum ErrorCode {
|
|
|
+ /// Invalid JSON was received by the server.
|
|
|
+ /// An error occurred on the server while parsing the JSON text.
|
|
|
ParseError,
|
|
|
+ /// The JSON sent is not a valid Request object.
|
|
|
InvalidRequest,
|
|
|
+ /// The method does not exist / is not available.
|
|
|
MethodNotFound,
|
|
|
+ /// Invalid method parameter(s).
|
|
|
InvalidParams,
|
|
|
+ /// Internal JSON-RPC error.
|
|
|
InternalError,
|
|
|
- ServerError(i64),
|
|
|
- InvalidId,
|
|
|
+ /// ID mismatch
|
|
|
+ IdMismatch,
|
|
|
+ /// Invalid/Unexpected reply
|
|
|
+ InvalidReply,
|
|
|
+ /// Reserved for implementation-defined server-errors.
|
|
|
+ ServerError(i32),
|
|
|
}
|
|
|
|
|
|
impl ErrorCode {
|
|
|
- pub fn code(&self) -> i64 {
|
|
|
+ pub fn code(&self) -> i32 {
|
|
|
match *self {
|
|
|
Self::ParseError => -32700,
|
|
|
Self::InvalidRequest => -32600,
|
|
|
Self::MethodNotFound => -32601,
|
|
|
Self::InvalidParams => -32602,
|
|
|
Self::InternalError => -32603,
|
|
|
- // -32000 to -32099
|
|
|
+ Self::IdMismatch => -32360,
|
|
|
+ Self::InvalidReply => -32361,
|
|
|
Self::ServerError(c) => c,
|
|
|
- Self::InvalidId => -32001,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub fn desc(&self) -> String {
|
|
|
- let desc = match *self {
|
|
|
- Self::ParseError => "Parse error",
|
|
|
- Self::InvalidRequest => "Invalid request",
|
|
|
- Self::MethodNotFound => "Method not found",
|
|
|
- Self::InvalidParams => "Invalid params",
|
|
|
- Self::InternalError => "Internal error",
|
|
|
- Self::ServerError(_) => "",
|
|
|
- Self::InvalidId => "Request ID mismatch",
|
|
|
- };
|
|
|
-
|
|
|
- desc.to_string()
|
|
|
+ pub fn message(&self) -> String {
|
|
|
+ match *self {
|
|
|
+ Self::ParseError => "parse error".to_string(),
|
|
|
+ Self::InvalidRequest => "invalid request".to_string(),
|
|
|
+ Self::MethodNotFound => "method not found".to_string(),
|
|
|
+ Self::InvalidParams => "invalid params".to_string(),
|
|
|
+ Self::InternalError => "internal error".to_string(),
|
|
|
+ Self::IdMismatch => "id mismatch".to_string(),
|
|
|
+ Self::InvalidReply => "invalid reply".to_string(),
|
|
|
+ Self::ServerError(_) => "server error".to_string(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn desc(&self) -> JsonValue {
|
|
|
+ JsonValue::String(self.message())
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// Wrapping enum around the possible JSON-RPC object types.
|
|
|
// ANCHOR: jsonresult
|
|
|
-#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
-#[serde(untagged)]
|
|
|
+/// Wrapping enum around the available JSON-RPC object types
|
|
|
+#[derive(Clone, Debug)]
|
|
|
pub enum JsonResult {
|
|
|
Response(JsonResponse),
|
|
|
Error(JsonError),
|
|
|
Notification(JsonNotification),
|
|
|
+ /// Subscriber is a special object that yields a channel
|
|
|
Subscriber(JsonSubscriber),
|
|
|
+ Request(JsonRequest),
|
|
|
+}
|
|
|
+
|
|
|
+impl JsonResult {
|
|
|
+ pub fn try_from_value(value: &JsonValue) -> Result<Self> {
|
|
|
+ if let Ok(response) = JsonResponse::try_from(value) {
|
|
|
+ return Ok(Self::Response(response))
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Ok(error) = JsonError::try_from(value) {
|
|
|
+ return Ok(Self::Error(error))
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Ok(notification) = JsonNotification::try_from(value) {
|
|
|
+ return Ok(Self::Notification(notification))
|
|
|
+ }
|
|
|
+
|
|
|
+ Err(RpcError::InvalidJson("Invalid JSON Result".to_string()).into())
|
|
|
+ }
|
|
|
}
|
|
|
-// ANCHOR_END: jsonresult
|
|
|
|
|
|
impl From<JsonResponse> for JsonResult {
|
|
|
fn from(resp: JsonResponse) -> Self {
|
|
|
@@ -105,158 +139,366 @@ impl From<JsonSubscriber> for JsonResult {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// A JSON-RPC request object.
|
|
|
// ANCHOR: jsonrequest
|
|
|
-#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
+/// A JSON-RPC request object
|
|
|
+#[derive(Clone, Debug)]
|
|
|
pub struct JsonRequest {
|
|
|
/// JSON-RPC version
|
|
|
- pub jsonrpc: Value,
|
|
|
+ pub jsonrpc: &'static str,
|
|
|
/// Request ID
|
|
|
- pub id: Value,
|
|
|
+ pub id: u16,
|
|
|
/// Request method
|
|
|
- pub method: Value,
|
|
|
+ pub method: String,
|
|
|
/// Request parameters
|
|
|
- pub params: Value,
|
|
|
+ pub params: JsonValue,
|
|
|
}
|
|
|
// ANCHOR_END: jsonrequest
|
|
|
|
|
|
impl JsonRequest {
|
|
|
- pub fn new(method: &str, parameters: Value) -> Self {
|
|
|
- Self {
|
|
|
- jsonrpc: json!("2.0"),
|
|
|
- id: json!(OsRng.gen::<u64>()),
|
|
|
- method: json!(method),
|
|
|
- params: parameters,
|
|
|
+ /// Create a new [`JsonRequest`] object with the given method and parameters.
|
|
|
+ /// The request ID is chosen randomly.
|
|
|
+ pub fn new(method: &str, params: JsonValue) -> Self {
|
|
|
+ assert!(params.is_array());
|
|
|
+ Self { jsonrpc: "2.0", id: OsRng::gen(&mut OsRng), method: method.to_string(), params }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Convert the object into a JSON string
|
|
|
+ pub fn stringify(&self) -> Result<String> {
|
|
|
+ let v: JsonValue = self.into();
|
|
|
+ Ok(v.stringify()?)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl From<&JsonRequest> for JsonValue {
|
|
|
+ fn from(req: &JsonRequest) -> JsonValue {
|
|
|
+ JsonValue::Object(HashMap::from([
|
|
|
+ ("jsonrpc".to_string(), JsonValue::String(req.jsonrpc.to_string())),
|
|
|
+ ("id".to_string(), JsonValue::Number(req.id.into())),
|
|
|
+ ("method".to_string(), JsonValue::String(req.method.clone())),
|
|
|
+ ("params".to_string(), req.params.clone()),
|
|
|
+ ]))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl TryFrom<&JsonValue> for JsonRequest {
|
|
|
+ type Error = RpcError;
|
|
|
+
|
|
|
+ fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
|
|
|
+ if !value.is_object() {
|
|
|
+ return Err(RpcError::InvalidJson("JSON is not an Object".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ let map: &HashMap<String, JsonValue> = value.get().unwrap();
|
|
|
+
|
|
|
+ if !map.contains_key("jsonrpc") ||
|
|
|
+ !map["jsonrpc"].is_string() ||
|
|
|
+ map["jsonrpc"] != JsonValue::String("2.0".to_string())
|
|
|
+ {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Request does not contain valid \"jsonrpc\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.contains_key("id") || !map["id"].is_number() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Request does not contain valid \"id\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.contains_key("method") || !map["method"].is_string() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Request does not contain valid \"method\" field".to_string(),
|
|
|
+ ))
|
|
|
}
|
|
|
+
|
|
|
+ if !map.contains_key("params") || !map["params"].is_array() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Request does not contain valid \"params\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(Self {
|
|
|
+ jsonrpc: "2.0",
|
|
|
+ id: *map["id"].get::<f64>().unwrap() as u16,
|
|
|
+ method: map["method"].get::<String>().unwrap().clone(),
|
|
|
+ params: map["params"].clone(),
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// A JSON-RPC notification object.
|
|
|
-#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
+/// A JSON-RPC notification object
|
|
|
+#[derive(Clone, Debug)]
|
|
|
pub struct JsonNotification {
|
|
|
/// JSON-RPC version
|
|
|
- pub jsonrpc: Value,
|
|
|
+ pub jsonrpc: &'static str,
|
|
|
/// Notification method
|
|
|
- pub method: Value,
|
|
|
+ pub method: String,
|
|
|
/// Notification parameters
|
|
|
- pub params: Value,
|
|
|
+ pub params: JsonValue,
|
|
|
}
|
|
|
|
|
|
impl JsonNotification {
|
|
|
- pub fn new(method: Value, params: Value) -> Self {
|
|
|
- Self { jsonrpc: json!("2.0"), method, params }
|
|
|
+ /// Create a new [`JsonNotification`] object with the given method and parameters.
|
|
|
+ pub fn new(method: &str, params: JsonValue) -> Self {
|
|
|
+ assert!(params.is_array());
|
|
|
+ Self { jsonrpc: "2.0", method: method.to_string(), params }
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-/// A method specific JSON-RPC subscriber for notifications
|
|
|
-#[derive(Clone)]
|
|
|
-pub struct MethodSubscriber {
|
|
|
- /// Notification method
|
|
|
- pub method: Value,
|
|
|
- /// Notification subscriber
|
|
|
- pub subscriber: SubscriberPtr<JsonNotification>,
|
|
|
-}
|
|
|
-
|
|
|
-impl MethodSubscriber {
|
|
|
- pub fn new(method: Value) -> Self {
|
|
|
- let subscriber = Subscriber::new();
|
|
|
- Self { method, subscriber }
|
|
|
+ /// Convert the object into a JSON string
|
|
|
+ pub fn stringify(&self) -> Result<String> {
|
|
|
+ let v: JsonValue = self.into();
|
|
|
+ Ok(v.stringify()?)
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- /// Auxiliary function to format provided message and notify the subscriber.
|
|
|
- pub async fn notify<T: Encodable>(&self, message: &T) {
|
|
|
- let params = json!([bs58::encode(&serialize(message)).into_string()]);
|
|
|
- let notif = JsonNotification::new(self.method.clone(), params);
|
|
|
- self.subscriber.notify(notif).await;
|
|
|
+impl From<&JsonNotification> for JsonValue {
|
|
|
+ fn from(notif: &JsonNotification) -> JsonValue {
|
|
|
+ JsonValue::Object(HashMap::from([
|
|
|
+ ("jsonrpc".to_string(), JsonValue::String(notif.jsonrpc.to_string())),
|
|
|
+ ("method".to_string(), JsonValue::String(notif.method.clone())),
|
|
|
+ ("params".to_string(), notif.params.clone()),
|
|
|
+ ]))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl fmt::Debug for MethodSubscriber {
|
|
|
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
- f.debug_struct("MethodSubscriber")
|
|
|
- .field("method", &self.method)
|
|
|
- .field("pointer", &Arc::as_ptr(&self.subscriber))
|
|
|
- .finish()
|
|
|
+impl TryFrom<&JsonValue> for JsonNotification {
|
|
|
+ type Error = RpcError;
|
|
|
+
|
|
|
+ fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
|
|
|
+ if !value.is_object() {
|
|
|
+ return Err(RpcError::InvalidJson("JSON is not an Object".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ let map: &HashMap<String, JsonValue> = value.get().unwrap();
|
|
|
+
|
|
|
+ if !map.contains_key("jsonrpc") ||
|
|
|
+ !map["jsonrpc"].is_string() ||
|
|
|
+ map["jsonrpc"] != JsonValue::String("2.0".to_string())
|
|
|
+ {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Notification does not contain valid \"jsonrpc\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.contains_key("method") || !map["method"].is_string() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Notification does not contain valid \"method\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.contains_key("params") || !map["params"].is_array() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Notification does not contain valid \"params\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(Self {
|
|
|
+ jsonrpc: "2.0",
|
|
|
+ method: map["method"].get::<String>().unwrap().clone(),
|
|
|
+ params: map["params"].clone(),
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// A JSON-RPC subscriber for notifications
|
|
|
+/// A JSON-RPC response object
|
|
|
#[derive(Clone, Debug)]
|
|
|
-pub struct JsonSubscriber {
|
|
|
+pub struct JsonResponse {
|
|
|
/// JSON-RPC version
|
|
|
- pub jsonrpc: Value,
|
|
|
- /// Method subscriber
|
|
|
- pub subscriber: MethodSubscriber,
|
|
|
+ pub jsonrpc: &'static str,
|
|
|
+ /// Request ID
|
|
|
+ pub id: u16,
|
|
|
+ /// Response result
|
|
|
+ pub result: JsonValue,
|
|
|
}
|
|
|
|
|
|
-impl JsonSubscriber {
|
|
|
- pub fn new(subscriber: MethodSubscriber) -> Self {
|
|
|
- Self { jsonrpc: json!("2.0"), subscriber }
|
|
|
+impl JsonResponse {
|
|
|
+ /// Create a new [`JsonResponse`] object with the given ID and result value.
|
|
|
+ /// Creating a `JsonResponse` implies that the method call was successful.
|
|
|
+ pub fn new(result: JsonValue, id: u16) -> Self {
|
|
|
+ Self { jsonrpc: "2.0", id, result }
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-impl Serialize for JsonSubscriber {
|
|
|
- fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
|
|
|
- where
|
|
|
- S: Serializer,
|
|
|
- {
|
|
|
- unimplemented!();
|
|
|
+ /// Convert the object into a JSON string
|
|
|
+ pub fn stringify(&self) -> Result<String> {
|
|
|
+ let v: JsonValue = self.into();
|
|
|
+ Ok(v.stringify()?)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl<'de> Deserialize<'de> for JsonSubscriber {
|
|
|
- fn deserialize<D>(_deserializer: D) -> Result<JsonSubscriber, D::Error>
|
|
|
- where
|
|
|
- D: Deserializer<'de>,
|
|
|
- {
|
|
|
- unimplemented!();
|
|
|
+impl From<&JsonResponse> for JsonValue {
|
|
|
+ fn from(rep: &JsonResponse) -> JsonValue {
|
|
|
+ JsonValue::Object(HashMap::from([
|
|
|
+ ("jsonrpc".to_string(), JsonValue::String(rep.jsonrpc.to_string())),
|
|
|
+ ("id".to_string(), JsonValue::Number(rep.id.into())),
|
|
|
+ ("result".to_string(), rep.result.clone()),
|
|
|
+ ]))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// A JSON-RPC response object.
|
|
|
-#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
-pub struct JsonResponse {
|
|
|
- /// JSON-RPC version
|
|
|
- pub jsonrpc: Value,
|
|
|
- /// Request ID
|
|
|
- pub id: Value,
|
|
|
- /// Response result
|
|
|
- pub result: Value,
|
|
|
-}
|
|
|
+impl TryFrom<&JsonValue> for JsonResponse {
|
|
|
+ type Error = RpcError;
|
|
|
|
|
|
-impl JsonResponse {
|
|
|
- pub fn new(result: Value, id: Value) -> Self {
|
|
|
- Self { jsonrpc: json!("2.0"), id, result }
|
|
|
+ fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
|
|
|
+ if !value.is_object() {
|
|
|
+ return Err(RpcError::InvalidJson("Json is not an Object".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ let map: &HashMap<String, JsonValue> = value.get().unwrap();
|
|
|
+
|
|
|
+ if !map.contains_key("jsonrpc") ||
|
|
|
+ !map["jsonrpc"].is_string() ||
|
|
|
+ map["jsonrpc"] != JsonValue::String("2.0".to_string())
|
|
|
+ {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Response does not contain valid \"jsonrpc\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.contains_key("id") || !map["id"].is_number() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Response does not contain valid \"id\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(Self {
|
|
|
+ jsonrpc: "2.0",
|
|
|
+ id: *map["id"].get::<f64>().unwrap() as u16,
|
|
|
+ result: map["result"].clone(),
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// A JSON-RPC error object.
|
|
|
-#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
+/// A JSON-RPC error object
|
|
|
+#[derive(Clone, Debug)]
|
|
|
pub struct JsonError {
|
|
|
/// JSON-RPC version
|
|
|
- pub jsonrpc: Value,
|
|
|
+ pub jsonrpc: &'static str,
|
|
|
/// Request ID
|
|
|
- pub id: Value,
|
|
|
+ pub id: u16,
|
|
|
/// JSON-RPC error (code and message)
|
|
|
pub error: JsonErrorVal,
|
|
|
}
|
|
|
|
|
|
/// A JSON-RPC error value (code and message)
|
|
|
-#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
+#[derive(Clone, Debug)]
|
|
|
pub struct JsonErrorVal {
|
|
|
/// Error code
|
|
|
- pub code: Value,
|
|
|
+ pub code: i32,
|
|
|
/// Error message
|
|
|
- pub message: Value,
|
|
|
+ pub message: String,
|
|
|
}
|
|
|
|
|
|
impl JsonError {
|
|
|
- pub fn new(c: ErrorCode, m: Option<String>, id: Value) -> Self {
|
|
|
- let error = JsonErrorVal {
|
|
|
- code: json!(c.code()),
|
|
|
- message: if m.is_none() { json!(c.desc()) } else { json!(m.unwrap()) },
|
|
|
- };
|
|
|
+ /// Create a new [`JsonError`] object with the given error code, optional
|
|
|
+ /// message, and a response ID.
|
|
|
+ /// Creating a `JsonError` implies that the method call was unsuccessful.
|
|
|
+ pub fn new(c: ErrorCode, message: Option<String>, id: u16) -> Self {
|
|
|
+ let error = JsonErrorVal { code: c.code(), message: message.unwrap_or(c.message()) };
|
|
|
+ Self { jsonrpc: "2.0", id, error }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Convert the object into a JSON string
|
|
|
+ pub fn stringify(&self) -> Result<String> {
|
|
|
+ let v: JsonValue = self.into();
|
|
|
+ Ok(v.stringify()?)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl From<&JsonError> for JsonValue {
|
|
|
+ fn from(err: &JsonError) -> JsonValue {
|
|
|
+ let errmap = JsonValue::Object(HashMap::from([
|
|
|
+ ("code".to_string(), JsonValue::Number(err.error.code.into())),
|
|
|
+ ("message".to_string(), JsonValue::String(err.error.message.clone())),
|
|
|
+ ]));
|
|
|
+
|
|
|
+ JsonValue::Object(HashMap::from([
|
|
|
+ ("jsonrpc".to_string(), JsonValue::String(err.jsonrpc.to_string())),
|
|
|
+ ("id".to_string(), JsonValue::Number(err.id.into())),
|
|
|
+ ("error".to_string(), errmap),
|
|
|
+ ]))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl TryFrom<&JsonValue> for JsonError {
|
|
|
+ type Error = RpcError;
|
|
|
+
|
|
|
+ fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
|
|
|
+ if !value.is_object() {
|
|
|
+ return Err(RpcError::InvalidJson("JSON is not an Object".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ let map: &HashMap<String, JsonValue> = value.get().unwrap();
|
|
|
+
|
|
|
+ if !map.contains_key("jsonrpc") ||
|
|
|
+ !map["jsonrpc"].is_string() ||
|
|
|
+ map["jsonrpc"] != JsonValue::String("2.0".to_string())
|
|
|
+ {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Error does not contain valid \"jsonrpc\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.contains_key("id") || !map["id"].is_number() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Error does not contain valid \"id\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.contains_key("error") || !map["error"].is_object() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Error does not contain valid \"error\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map["error"]["code"].is_number() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Error does not contain valid \"error.code\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map["error"]["message"].is_string() {
|
|
|
+ return Err(RpcError::InvalidJson(
|
|
|
+ "Error does not contain valid \"error.message\" field".to_string(),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(Self {
|
|
|
+ jsonrpc: "2.0",
|
|
|
+ id: *map["id"].get::<f64>().unwrap() as u16,
|
|
|
+ error: JsonErrorVal {
|
|
|
+ code: *map["error"]["code"].get::<f64>().unwrap() as i32,
|
|
|
+ message: map["error"]["message"].get::<String>().unwrap().to_string(),
|
|
|
+ },
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// A JSON-RPC subscriber for notifications
|
|
|
+#[derive(Clone, Debug)]
|
|
|
+pub struct JsonSubscriber {
|
|
|
+ /// Notification method
|
|
|
+ pub method: &'static str,
|
|
|
+ /// Notification subscriber
|
|
|
+ pub sub: SubscriberPtr<JsonNotification>,
|
|
|
+}
|
|
|
+
|
|
|
+impl JsonSubscriber {
|
|
|
+ pub fn new(method: &'static str) -> Self {
|
|
|
+ let sub = Subscriber::new();
|
|
|
+ Self { method, sub }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Send a notification to the subscriber with the given params.
|
|
|
+ /// All the params will be serialized and then encoded with base64 encoding.
|
|
|
+ pub async fn notify<T: Encodable>(&self, raw_params: &[T]) {
|
|
|
+ let mut params = vec![];
|
|
|
+
|
|
|
+ // Serialize and encode all params
|
|
|
+ for raw_param in raw_params {
|
|
|
+ params.push(JsonValue::String(base64::encode(&serialize(raw_param))));
|
|
|
+ }
|
|
|
|
|
|
- Self { jsonrpc: json!("2.0"), error, id }
|
|
|
+ let notification = JsonNotification::new(self.method, JsonValue::Array(params));
|
|
|
+ self.sub.notify(notification).await;
|
|
|
}
|
|
|
}
|