1use std::sync::atomic::{AtomicU64, Ordering};
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8static NEXT_ID: AtomicU64 = AtomicU64::new(1);
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13#[serde(untagged)]
14pub enum RpcId {
15 Number(u64),
16 String(String),
17 Null,
18}
19
20impl RpcId {
21 pub fn number(n: u64) -> Self {
22 Self::Number(n)
23 }
24}
25
26impl std::fmt::Display for RpcId {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 Self::Number(n) => write!(f, "{n}"),
30 Self::String(s) => write!(f, "{s}"),
31 Self::Null => write!(f, "null"),
32 }
33 }
34}
35
36pub type RpcParam = Value;
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct JsonRpcRequest {
42 pub jsonrpc: String,
43 pub method: String,
44 pub params: Vec<RpcParam>,
45 pub id: RpcId,
46}
47
48impl JsonRpcRequest {
49 pub fn new(id: u64, method: impl Into<String>, params: Vec<RpcParam>) -> Self {
51 Self {
52 jsonrpc: "2.0".into(),
53 method: method.into(),
54 params,
55 id: RpcId::Number(id),
56 }
57 }
58
59 pub fn auto(method: impl Into<String>, params: Vec<RpcParam>) -> Self {
61 let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
62 Self::new(id, method, params)
63 }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct JsonRpcError {
69 pub code: i64,
70 pub message: String,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub data: Option<Value>,
73}
74
75impl std::fmt::Display for JsonRpcError {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 write!(f, "JSON-RPC error {}: {}", self.code, self.message)
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct JsonRpcResponse {
84 pub jsonrpc: String,
85 pub id: RpcId,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub result: Option<Value>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub error: Option<JsonRpcError>,
90}
91
92impl JsonRpcResponse {
93 pub fn is_ok(&self) -> bool {
95 self.error.is_none() && self.result.is_some()
96 }
97
98 pub fn into_result(self) -> Result<Value, JsonRpcError> {
100 if let Some(err) = self.error {
101 Err(err)
102 } else {
103 Ok(self.result.unwrap_or(Value::Null))
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn request_serialization() {
114 let req = JsonRpcRequest::new(1, "eth_blockNumber", vec![]);
115 let json = serde_json::to_string(&req).unwrap();
116 assert!(json.contains("\"jsonrpc\":\"2.0\""));
117 assert!(json.contains("\"method\":\"eth_blockNumber\""));
118 }
119
120 #[test]
121 fn response_into_result_ok() {
122 let resp = JsonRpcResponse {
123 jsonrpc: "2.0".into(),
124 id: RpcId::Number(1),
125 result: Some(Value::String("0x12345".into())),
126 error: None,
127 };
128 assert!(resp.is_ok());
129 let val = resp.into_result().unwrap();
130 assert_eq!(val, Value::String("0x12345".into()));
131 }
132
133 #[test]
134 fn response_into_result_error() {
135 let resp = JsonRpcResponse {
136 jsonrpc: "2.0".into(),
137 id: RpcId::Number(1),
138 result: None,
139 error: Some(JsonRpcError {
140 code: -32000,
141 message: "execution reverted".into(),
142 data: None,
143 }),
144 };
145 assert!(!resp.is_ok());
146 let err = resp.into_result().unwrap_err();
147 assert_eq!(err.code, -32000);
148 }
149}