1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[cfg(not(feature = "mock-ffi"))]
6#[link(wasm_import_module = "bless")]
7extern "C" {
8 #[link_name = "rpc_call"]
9 fn rpc_call(
10 request_ptr: *const u8,
11 request_len: u32,
12 response_ptr: *mut u8,
13 response_max_len: u32,
14 bytes_written: *mut u32,
15 ) -> u32;
16}
17
18#[cfg(feature = "mock-ffi")]
19#[allow(unused_variables)]
20mod mock_ffi {
21 pub unsafe fn rpc_call(
22 _request_ptr: *const u8,
23 _request_len: u32,
24 _response_ptr: *mut u8,
25 _response_max_len: u32,
26 _bytes_written: *mut u32,
27 ) -> u32 {
28 0
30 }
31}
32
33#[cfg(feature = "mock-ffi")]
34use mock_ffi::*;
35
36#[derive(Debug, Clone)]
37pub enum RpcError {
38 InvalidJson,
39 MethodNotFound,
40 InvalidParams,
41 InternalError,
42 BufferTooSmall,
43 Utf8Error,
44}
45
46impl std::fmt::Display for RpcError {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 RpcError::InvalidJson => write!(f, "Invalid JSON format"),
50 RpcError::MethodNotFound => write!(f, "Method not found"),
51 RpcError::InvalidParams => write!(f, "Invalid parameters"),
52 RpcError::InternalError => write!(f, "Internal error"),
53 RpcError::BufferTooSmall => write!(f, "Buffer too small"),
54 RpcError::Utf8Error => write!(f, "UTF-8 conversion error"),
55 }
56 }
57}
58
59impl std::error::Error for RpcError {}
60
61#[derive(Serialize, Deserialize, Debug)]
63pub struct JsonRpcRequest<T> {
64 pub jsonrpc: String,
65 pub method: String,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub params: Option<T>,
68 pub id: u32,
69}
70
71#[derive(Serialize, Deserialize, Debug)]
72pub struct JsonRpcResponse<T> {
73 pub jsonrpc: String,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub result: Option<T>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub error: Option<JsonRpcError>,
78 pub id: u32,
79}
80
81#[derive(Serialize, Deserialize, Debug)]
82pub struct JsonRpcError {
83 pub code: i32,
84 pub message: String,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub data: Option<serde_json::Value>,
87}
88
89pub struct RpcClient {
91 next_id: u32,
92 buffer_size: usize,
93}
94
95impl Default for RpcClient {
96 fn default() -> Self {
97 Self::with_buffer_size(4096) }
99}
100
101impl RpcClient {
102 pub fn new() -> Self {
103 Self::default()
104 }
105
106 pub fn with_buffer_size(buffer_size: usize) -> Self {
107 Self {
108 next_id: 1,
109 buffer_size,
110 }
111 }
112
113 pub fn set_buffer_size(&mut self, buffer_size: usize) {
114 self.buffer_size = buffer_size;
115 }
116
117 pub fn buffer_size(&self) -> usize {
118 self.buffer_size
119 }
120
121 pub fn call<P: Serialize, R: serde::de::DeserializeOwned>(
122 &mut self,
123 method: &str,
124 params: Option<P>,
125 ) -> Result<JsonRpcResponse<R>, RpcError> {
126 let request = JsonRpcRequest {
127 jsonrpc: "2.0".to_string(),
128 method: method.to_string(),
129 params,
130 id: self.next_id,
131 };
132
133 self.next_id += 1;
134 let request_bytes = serde_json::to_vec(&request).map_err(|_| RpcError::InvalidJson)?;
135 let mut response_buffer = vec![0u8; self.buffer_size];
136 let mut bytes_written = 0u32;
137 let result = unsafe {
138 rpc_call(
139 request_bytes.as_ptr(),
140 request_bytes.len() as u32,
141 response_buffer.as_mut_ptr(),
142 response_buffer.len() as u32,
143 &mut bytes_written as *mut u32,
144 )
145 };
146 if result != 0 {
147 return match result {
148 1 => Err(RpcError::InvalidJson),
149 2 => Err(RpcError::MethodNotFound),
150 3 => Err(RpcError::InvalidParams),
151 4 => Err(RpcError::InternalError),
152 5 => Err(RpcError::BufferTooSmall),
153 _ => Err(RpcError::InternalError),
154 };
155 }
156 response_buffer.truncate(bytes_written as usize);
157 serde_json::from_slice(&response_buffer).map_err(|_| RpcError::InvalidJson)
158 }
159
160 pub fn ping(&mut self) -> Result<String, RpcError> {
162 let response: JsonRpcResponse<String> = self.call("ping", None::<()>)?;
163 response.result.ok_or(RpcError::InternalError)
164 }
165
166 pub fn echo<T: Serialize + serde::de::DeserializeOwned>(
168 &mut self,
169 data: T,
170 ) -> Result<T, RpcError> {
171 let response: JsonRpcResponse<T> = self.call("echo", Some(data))?;
172 response.result.ok_or(RpcError::InternalError)
173 }
174
175 pub fn version(&mut self) -> Result<HashMap<String, String>, RpcError> {
177 let response: JsonRpcResponse<HashMap<String, String>> =
178 self.call("version", None::<()>)?;
179 response.result.ok_or(RpcError::InternalError)
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn test_rpc_request_serialization() {
189 let request: JsonRpcRequest<()> = JsonRpcRequest {
190 jsonrpc: "2.0".to_string(),
191 method: "ping".to_string(),
192 params: None,
193 id: 1,
194 };
195
196 let json_str = serde_json::to_string(&request).unwrap();
197 assert!(json_str.contains("\"jsonrpc\":\"2.0\""));
198 assert!(json_str.contains("\"method\":\"ping\""));
199 assert!(json_str.contains("\"id\":1"));
200 }
201
202 #[test]
203 fn test_rpc_response_deserialization() {
204 let json_str = r#"{"jsonrpc":"2.0","result":"pong","id":1}"#;
205 let response: JsonRpcResponse<String> = serde_json::from_str(json_str).unwrap();
206
207 assert_eq!(response.jsonrpc, "2.0");
208 assert_eq!(response.result, Some("pong".to_string()));
209 assert_eq!(response.id, 1);
210 assert!(response.error.is_none());
211 }
212}