use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RpcMethodCall {
pub jsonrpc: Version,
pub method: String,
#[serde(default = "no_params")]
pub params: RequestParams,
pub id: Id,
}
impl RpcMethodCall {
pub fn id(&self) -> Id {
self.id.clone()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RpcNotification {
pub jsonrpc: Option<Version>,
pub method: String,
#[serde(default = "no_params")]
pub params: RequestParams,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RpcCall {
MethodCall(RpcMethodCall),
Notification(RpcNotification),
Invalid {
#[serde(default = "null_id")]
id: Id,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum Request {
Single(RpcCall),
Batch(Vec<RpcCall>),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged, deny_unknown_fields)]
pub enum RequestParams {
None,
Array(Vec<serde_json::Value>),
Object(serde_json::Map<String, serde_json::Value>),
}
impl From<RequestParams> for serde_json::Value {
fn from(params: RequestParams) -> Self {
match params {
RequestParams::None => Self::Null,
RequestParams::Array(arr) => arr.into(),
RequestParams::Object(obj) => obj.into(),
}
}
}
fn no_params() -> RequestParams {
RequestParams::None
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Version {
#[serde(rename = "2.0")]
V2,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Id {
String(String),
Number(i64),
Null,
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::String(s) => s.fmt(f),
Self::Number(n) => n.fmt(f),
Self::Null => f.write_str("null"),
}
}
}
fn null_id() -> Id {
Id::Null
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_serialize_batch() {
let batch = Request::Batch(vec![
RpcCall::MethodCall(RpcMethodCall {
jsonrpc: Version::V2,
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![
serde_json::Value::from(999),
serde_json::Value::from(1337),
]),
id: Id::Number(1),
}),
RpcCall::Notification(RpcNotification {
jsonrpc: Some(Version::V2),
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![serde_json::Value::from(999)]),
}),
]);
let obj = serde_json::to_string(&batch).unwrap();
assert_eq!(
obj,
r#"[{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1},{"jsonrpc":"2.0","method":"eth_method","params":[999]}]"#
);
}
#[test]
fn can_deserialize_batch() {
let s = r#"[{}, {"jsonrpc": "2.0", "method": "eth_call", "params": [1337,420], "id": 1},{"jsonrpc": "2.0", "method": "notify", "params": [999]}]"#;
let obj: Request = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
Request::Batch(vec![
RpcCall::Invalid { id: Id::Null },
RpcCall::MethodCall(RpcMethodCall {
jsonrpc: Version::V2,
method: "eth_call".to_owned(),
params: RequestParams::Array(vec![
serde_json::Value::from(1337),
serde_json::Value::from(420)
]),
id: Id::Number(1)
}),
RpcCall::Notification(RpcNotification {
jsonrpc: Some(Version::V2),
method: "notify".to_owned(),
params: RequestParams::Array(vec![serde_json::Value::from(999)])
})
])
)
}
#[test]
fn can_serialize_method() {
let m = RpcMethodCall {
jsonrpc: Version::V2,
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![
serde_json::Value::from(999),
serde_json::Value::from(1337),
]),
id: Id::Number(1),
};
let obj = serde_json::to_string(&m).unwrap();
assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1}"#);
}
#[test]
fn can_serialize_call_notification() {
let n = RpcCall::Notification(RpcNotification {
jsonrpc: Some(Version::V2),
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![serde_json::Value::from(999)]),
});
let obj = serde_json::to_string(&n).unwrap();
assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999]}"#);
}
#[test]
fn can_serialize_notification() {
let n = RpcNotification {
jsonrpc: Some(Version::V2),
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![
serde_json::Value::from(999),
serde_json::Value::from(1337),
]),
};
let obj = serde_json::to_string(&n).unwrap();
assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337]}"#);
}
#[test]
fn can_deserialize_notification() {
let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337]}"#;
let obj: RpcNotification = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
RpcNotification {
jsonrpc: Some(Version::V2),
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![
serde_json::Value::from(999),
serde_json::Value::from(1337)
])
}
);
let s = r#"{"jsonrpc": "2.0", "method": "foobar"}"#;
let obj: RpcNotification = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
RpcNotification {
jsonrpc: Some(Version::V2),
method: "foobar".to_owned(),
params: RequestParams::None,
}
);
let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337], "id": 1}"#;
let obj: Result<RpcNotification, _> = serde_json::from_str(s);
assert!(obj.is_err());
}
#[test]
fn can_deserialize_call() {
let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999]}"#;
let obj: RpcCall = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
RpcCall::Notification(RpcNotification {
jsonrpc: Some(Version::V2),
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![serde_json::Value::from(999)])
})
);
let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999], "id": 1}"#;
let obj: RpcCall = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
RpcCall::MethodCall(RpcMethodCall {
jsonrpc: Version::V2,
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![serde_json::Value::from(999)]),
id: Id::Number(1)
})
);
let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [], "id": 1}"#;
let obj: RpcCall = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
RpcCall::MethodCall(RpcMethodCall {
jsonrpc: Version::V2,
method: "eth_method".to_owned(),
params: RequestParams::Array(vec![]),
id: Id::Number(1)
})
);
let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": null, "id": 1}"#;
let obj: RpcCall = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
RpcCall::MethodCall(RpcMethodCall {
jsonrpc: Version::V2,
method: "eth_method".to_owned(),
params: RequestParams::None,
id: Id::Number(1)
})
);
let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "id": 1}"#;
let obj: RpcCall = serde_json::from_str(s).unwrap();
assert_eq!(
obj,
RpcCall::MethodCall(RpcMethodCall {
jsonrpc: Version::V2,
method: "eth_method".to_owned(),
params: RequestParams::None,
id: Id::Number(1)
})
);
}
}