use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
use serde::{Deserialize, Serialize};
use serde_json::Value;
static RPC_ID: AtomicU64 = AtomicU64::new(0);
fn next_id() -> u64 {
RPC_ID.fetch_add(1, Ordering::SeqCst) + 1
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
pub jsonrpc: String,
pub method: String,
pub params: Value,
pub id: Value,
}
impl Request {
pub fn new(method: impl Into<String>, params: Vec<Value>) -> Request {
Request {
jsonrpc: "2.0".to_string(),
method: method.into(),
params: Value::Array(params),
id: Value::from(next_id()),
}
}
pub fn with_map(method: impl Into<String>, params: serde_json::Map<String, Value>) -> Request {
Request {
jsonrpc: "2.0".to_string(),
method: method.into(),
params: Value::Object(params),
id: Value::from(next_id()),
}
}
pub(crate) fn make_error(&self, e: &crate::Error) -> ResponseIntf {
let error = match e {
crate::Error::Rpc(eo) => eo.clone(),
other => ErrorObject {
code: -32603,
message: other.to_string(),
data: None,
},
};
ResponseIntf {
jsonrpc: "2.0".to_string(),
result: None,
error: Some(error),
id: self.id.clone(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Response {
#[allow(dead_code)]
pub jsonrpc: String,
#[serde(default)]
pub result: Value,
#[serde(default)]
pub error: Option<ErrorObject>,
#[serde(default)]
pub id: Value,
}
#[derive(Debug, Clone, Serialize)]
pub struct ResponseIntf {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<ErrorObject>,
pub id: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorObject {
pub code: i64,
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
impl fmt::Display for ErrorObject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "jsonrpc error {}: {}", self.code, self.message)
}
}
impl std::error::Error for ErrorObject {}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn new_request_empty_params() {
let req = Request::new("eth_blockNumber", vec![]);
assert_eq!(req.jsonrpc, "2.0");
assert_eq!(req.method, "eth_blockNumber");
assert_eq!(req.params, json!([]));
assert!(req.id.as_u64().unwrap() > 0);
}
#[test]
fn new_request_with_params() {
let req = Request::new("eth_getBalance", vec![json!("0xdead"), json!("latest")]);
assert_eq!(req.params, json!(["0xdead", "latest"]));
}
#[test]
fn new_request_map() {
let mut m = serde_json::Map::new();
m.insert("to".to_string(), json!("0xdead"));
let req = Request::with_map("eth_call", m);
assert_eq!(req.method, "eth_call");
assert_eq!(req.params["to"], json!("0xdead"));
}
#[test]
fn ids_are_monotonic() {
let a = Request::new("a", vec![]).id.as_u64().unwrap();
let b = Request::new("b", vec![]).id.as_u64().unwrap();
assert!(b > a);
}
#[test]
fn error_object_display() {
let e = ErrorObject {
code: -32601,
message: "Method not found".to_string(),
data: None,
};
assert_eq!(e.to_string(), "jsonrpc error -32601: Method not found");
}
#[test]
fn make_error_generic_and_passthrough() {
let req = Request::new("eth_test", vec![]);
let resp = req.make_error(&crate::Error::Other("something broke".to_string()));
let eo = resp.error.unwrap();
assert_eq!(eo.code, -32603);
assert_eq!(eo.message, "something broke");
let original = ErrorObject {
code: -32601,
message: "Method not found".to_string(),
data: None,
};
let resp = req.make_error(&crate::Error::Rpc(original));
assert_eq!(resp.error.unwrap().code, -32601);
}
}