use crate::RpcId;
use crate::router::{CallError, CallResult, CallSuccess};
use crate::rpc_response::{RpcError, RpcResponseParsingError};
use serde::de::{MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum RpcResponse {
Success(RpcSuccessResponse),
Error(RpcErrorResponse),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RpcSuccessResponse {
pub id: RpcId,
pub result: Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RpcErrorResponse {
pub id: RpcId,
pub error: RpcError,
}
impl RpcResponse {
pub fn from_success(id: RpcId, result: Value) -> Self {
Self::Success(RpcSuccessResponse { id, result })
}
pub fn from_error(id: RpcId, error: RpcError) -> Self {
Self::Error(RpcErrorResponse { id, error })
}
}
impl RpcResponse {
pub fn is_success(&self) -> bool {
matches!(self, RpcResponse::Success(_))
}
pub fn is_error(&self) -> bool {
matches!(self, RpcResponse::Error(_))
}
pub fn id(&self) -> &RpcId {
match self {
RpcResponse::Success(r) => &r.id,
RpcResponse::Error(r) => &r.id,
}
}
pub fn into_parts(self) -> (RpcId, core::result::Result<Value, RpcError>) {
match self {
RpcResponse::Success(r) => (r.id, Ok(r.result)),
RpcResponse::Error(r) => (r.id, Err(r.error)),
}
}
}
impl From<CallSuccess> for RpcResponse {
fn from(call_success: CallSuccess) -> Self {
RpcResponse::from_success(call_success.id, call_success.value)
}
}
impl From<CallError> for RpcResponse {
fn from(call_error: CallError) -> Self {
let id = call_error.id.clone(); let error = RpcError::from(call_error); RpcResponse::from_error(id, error)
}
}
impl From<CallResult> for RpcResponse {
fn from(call_result: CallResult) -> Self {
match call_result {
Ok(call_success) => RpcResponse::from(call_success),
Err(call_error) => RpcResponse::from(call_error),
}
}
}
impl Serialize for RpcResponse {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(3))?;
map.serialize_entry("jsonrpc", "2.0")?;
match self {
RpcResponse::Success(RpcSuccessResponse { id, result }) => {
map.serialize_entry("id", id)?;
map.serialize_entry("result", result)?;
}
RpcResponse::Error(RpcErrorResponse { id, error }) => {
map.serialize_entry("id", id)?;
map.serialize_entry("error", error)?;
}
}
map.end()
}
}
impl<'de> Deserialize<'de> for RpcResponse {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct RpcResponseVisitor;
impl<'de> Visitor<'de> for RpcResponseVisitor {
type Value = RpcResponse;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a JSON-RPC 2.0 response object")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut version: Option<String> = None;
let mut id_val: Option<Value> = None;
let mut result_val: Option<Value> = None;
let mut error_val: Option<Value> = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"jsonrpc" => {
if version.is_some() {
return Err(serde::de::Error::duplicate_field("jsonrpc"));
}
version = Some(map.next_value()?);
}
"id" => {
if id_val.is_some() {
return Err(serde::de::Error::duplicate_field("id"));
}
id_val = Some(map.next_value()?);
}
"result" => {
if result_val.is_some() {
return Err(serde::de::Error::duplicate_field("result"));
}
result_val = Some(map.next_value()?);
}
"error" => {
if error_val.is_some() {
return Err(serde::de::Error::duplicate_field("error"));
}
error_val = Some(map.next_value()?);
}
_ => {
let _: Value = map.next_value()?;
}
}
}
let id_for_error = id_val.as_ref().and_then(|v| RpcId::from_value(v.clone()).ok());
match version.as_deref() {
Some("2.0") => {} Some(v) => {
return Err(serde::de::Error::custom(
RpcResponseParsingError::InvalidJsonRpcVersion {
id: id_for_error,
expected: "2.0",
actual: Some(Value::String(v.to_string())),
},
));
}
None => {
return Err(serde::de::Error::custom(
RpcResponseParsingError::MissingJsonRpcVersion { id: id_for_error },
));
}
};
let id = match id_val {
Some(v) => RpcId::from_value(v)
.map_err(|e| serde::de::Error::custom(RpcResponseParsingError::InvalidId(e)))?,
None => return Err(serde::de::Error::custom(RpcResponseParsingError::MissingId)),
};
match (result_val, error_val) {
(Some(result), None) => Ok(RpcResponse::Success(RpcSuccessResponse { id, result })),
(None, Some(error_value)) => {
let error: RpcError = serde_json::from_value(error_value)
.map_err(|e| serde::de::Error::custom(RpcResponseParsingError::InvalidErrorObject(e)))?;
Ok(RpcResponse::Error(RpcErrorResponse { id, error }))
}
(Some(_), Some(_)) => Err(serde::de::Error::custom(RpcResponseParsingError::BothResultAndError {
id: id.clone(),
})),
(None, None) => Err(serde::de::Error::custom(
RpcResponseParsingError::MissingResultAndError { id: id.clone() },
)),
}
}
}
deserializer.deserialize_map(RpcResponseVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Error as RouterError; use serde_json::{from_value, json, to_value};
type TestResult<T> = core::result::Result<T, Box<dyn std::error::Error>>;
fn create_call_error(id: impl Into<RpcId>, method: &str, error: RouterError) -> CallError {
CallError {
id: id.into(),
method: method.to_string(),
error,
}
}
#[test]
fn test_rpc_response_success_ser_de() -> TestResult<()> {
let id = RpcId::Number(1);
let result_val = json!({"data": "ok"});
let response = RpcResponse::from_success(id.clone(), result_val.clone());
let expected_json = json!({
"jsonrpc": "2.0",
"id": 1,
"result": {"data": "ok"}
});
let serialized_value = to_value(&response)?;
assert_eq!(serialized_value, expected_json);
let deserialized_response: RpcResponse = from_value(serialized_value)?;
assert_eq!(deserialized_response, response);
assert_eq!(deserialized_response.id(), &id);
assert!(deserialized_response.is_success());
assert!(!deserialized_response.is_error());
let (resp_id, resp_result) = deserialized_response.into_parts();
assert_eq!(resp_id, id);
assert_eq!(resp_result.unwrap(), result_val);
Ok(())
}
#[test]
fn test_rpc_response_error_ser_de() -> TestResult<()> {
let id = RpcId::String("req-abc".into());
let rpc_error = RpcError {
code: -32601,
message: "Method not found".to_string(),
data: Some(json!("method_name")),
};
let response = RpcResponse::from_error(id.clone(), rpc_error.clone());
let expected_json = json!({
"jsonrpc": "2.0",
"id": "req-abc",
"error": {
"code": -32601,
"message": "Method not found",
"data": "method_name"
}
});
let serialized_value = to_value(&response)?;
assert_eq!(serialized_value, expected_json);
let deserialized_response: RpcResponse = from_value(serialized_value)?;
assert_eq!(deserialized_response, response);
assert_eq!(deserialized_response.id(), &id);
assert!(!deserialized_response.is_success());
assert!(deserialized_response.is_error());
let (resp_id, resp_result) = deserialized_response.into_parts();
assert_eq!(resp_id, id);
assert_eq!(resp_result.unwrap_err(), rpc_error);
Ok(())
}
#[test]
fn test_rpc_response_error_ser_de_no_data() -> TestResult<()> {
let id = RpcId::Null;
let rpc_error = RpcError {
code: -32700,
message: "Parse error".to_string(),
data: None, };
let response = RpcResponse::from_error(id.clone(), rpc_error.clone());
let expected_json = json!({
"jsonrpc": "2.0",
"id": null,
"error": {
"code": -32700,
"message": "Parse error"
}
});
let serialized_value = to_value(&response)?;
assert_eq!(serialized_value, expected_json);
let deserialized_response: RpcResponse = from_value(serialized_value)?;
assert_eq!(deserialized_response, response);
assert_eq!(deserialized_response.id(), &id);
assert!(deserialized_response.is_error());
let (resp_id, resp_result) = deserialized_response.into_parts();
assert_eq!(resp_id, id);
assert_eq!(resp_result.unwrap_err(), rpc_error);
Ok(())
}
#[test]
fn test_rpc_response_de_invalid() {
let invalid_jsons = vec![
json!({"id": 1, "result": "ok"}),
json!({"jsonrpc": "1.0", "id": 1, "result": "ok"}),
json!({"jsonrpc": "2.0", "result": "ok"}),
json!({"jsonrpc": "2.0", "id": 1}),
json!({"jsonrpc": "2.0", "id": 1, "result": "ok", "error": {"code": 1, "message": "err"}}),
json!({"jsonrpc": "2.0", "id": 1, "error": "not an object"}),
json!({"jsonrpc": "2.0", "id": 1, "error": {"message": "err"}}),
json!({"jsonrpc": "2.0", "id": 1, "error": {"code": 1}}),
json!({"jsonrpc": "2.0", "id": [1,2], "result": "ok"}),
];
for json_value in invalid_jsons {
let result: Result<RpcResponse, _> = from_value(json_value.clone());
assert!(result.is_err(), "Expected error for invalid JSON: {}", json_value);
}
}
#[test]
fn test_from_call_success() -> TestResult<()> {
let call_success = CallSuccess {
id: RpcId::Number(101),
method: "test_method".to_string(),
value: json!({"success": true}),
};
let rpc_response = RpcResponse::from(call_success);
match rpc_response {
RpcResponse::Success(RpcSuccessResponse { id, result }) => {
assert_eq!(id, RpcId::Number(101));
assert_eq!(result, json!({"success": true}));
}
RpcResponse::Error(_) => panic!("Expected RpcResponse::Success"),
}
Ok(())
}
#[test]
fn test_from_call_error() -> TestResult<()> {
let call_error = create_call_error(102, "test_method", RouterError::MethodUnknown);
let rpc_response = RpcResponse::from(call_error);
match rpc_response {
RpcResponse::Error(RpcErrorResponse { id, error }) => {
assert_eq!(id, RpcId::Number(102));
assert_eq!(error.code, RpcError::CODE_METHOD_NOT_FOUND);
assert_eq!(error.message, "Method not found");
assert!(error.data.is_some()); }
RpcResponse::Success(_) => panic!("Expected RpcResponse::Error"),
}
Ok(())
}
#[test]
fn test_from_call_result_ok() -> TestResult<()> {
let call_result: CallResult = Ok(CallSuccess {
id: 103.into(),
method: "test_method".to_string(),
value: json!("ok_data"),
});
let rpc_response = RpcResponse::from(call_result);
match rpc_response {
RpcResponse::Success(RpcSuccessResponse { id, result }) => {
assert_eq!(id, RpcId::Number(103));
assert_eq!(result, json!("ok_data"));
}
RpcResponse::Error(_) => panic!("Expected RpcResponse::Success"),
}
Ok(())
}
#[test]
fn test_from_call_result_err() -> TestResult<()> {
let call_result: CallResult = Err(create_call_error(
"err-104",
"test_method",
RouterError::ParamsMissingButRequested,
));
let rpc_response = RpcResponse::from(call_result);
match rpc_response {
RpcResponse::Error(RpcErrorResponse { id, error }) => {
assert_eq!(id, RpcId::String("err-104".into()));
assert_eq!(error.code, RpcError::CODE_INVALID_PARAMS);
assert_eq!(error.message, "Invalid params");
assert!(error.data.is_some()); }
RpcResponse::Success(_) => panic!("Expected RpcResponse::Error"),
}
Ok(())
}
}