gl_client/lsps/
json_rpc_erased.rs

1// The json-rpc implementation used in json_rpc is strongly typed and heavily
2// relies on Generics. In Rust all the required types are created at compile type.
3//
4// This is, Vec<u8> and Vec<u32> are considered 2 different types.
5//
6// When creating language bindings we must either
7// - explicitly implement ffi for every type.
8// - use a trait that erases tye type. In this case we replace the type by Vec<u8>. This byte-array can be parsed by the foreign language
9//
10// Note, that this
11//
12// If you are a rust-developer you probably want to use the json_rpc-module directly
13// If you are building a foreign function interface you probably want to use the type-erased version
14//
15// The JsonRpcMethodErased wraps the JsonRpcMethod into a type that works with Vec<u8>.
16// The JsonRpcMethodErased is object-safe and can be owned using a Box.
17//
18// The JsonRpcMethodErased method
19// - does not do strict type-checking at compile-time
20// - comes at a small runtime cost (requires Box, serializes and deserializes some objects twice, unwrapping results)
21// - comes at a small dev-cost for requiring a bit more error-handling
22
23use crate::lsps::json_rpc::{
24    ErrorData, JsonRpcMethod, JsonRpcRequest, JsonRpcResponse, JsonRpcResponseFailure,
25    JsonRpcResponseSuccess, MapErrorCode,
26};
27use serde::Serialize;
28
29pub type JsonRpcRequestErased = JsonRpcRequest<Vec<u8>>;
30pub type JsonRpcResponseErased = JsonRpcResponse<Vec<u8>, Vec<u8>>;
31pub type JsonRpcResponseSuccessErased = JsonRpcResponseSuccess<Vec<u8>>;
32pub type JsonRpcResponseFailureErased = JsonRpcResponseFailure<Vec<u8>>;
33pub type JsonRpcErrorDataErased = ErrorData<Vec<u8>>;
34
35pub trait JsonRpcMethodErased {
36    fn name(&self) -> &str;
37
38    fn create_request(
39        &self,
40        params: Vec<u8>,
41        json_rpc_id: String,
42    ) -> Result<JsonRpcRequestErased, serde_json::Error>;
43
44    fn parse_json_response_str(
45        &self,
46        json_str: &str,
47    ) -> Result<JsonRpcResponseErased, serde_json::Error>;
48
49    fn parse_json_response_value(
50        &self,
51        json_str: serde_json::Value,
52    ) -> Result<JsonRpcResponseErased, serde_json::Error>;
53}
54
55impl<I, O, E> JsonRpcMethodErased for JsonRpcMethod<I, O, E>
56where
57    I: serde::de::DeserializeOwned + Serialize,
58    O: serde::de::DeserializeOwned + Serialize,
59    E: serde::de::DeserializeOwned + Serialize + MapErrorCode,
60{
61    fn name(&self) -> &str {
62        self.method
63    }
64
65    fn create_request(
66        &self,
67        params: Vec<u8>,
68        json_rpc_id: String,
69    ) -> Result<JsonRpcRequestErased, serde_json::Error> {
70        let typed_params: I = serde_json::from_slice(&params)?;
71        JsonRpcMethod::create_request(self, typed_params, json_rpc_id).erase()
72    }
73
74    fn parse_json_response_str(
75        &self,
76        json_str: &str,
77    ) -> Result<JsonRpcResponseErased, serde_json::Error> {
78        // Check if the json-struct matches the expected type
79        JsonRpcMethod::<I, O, E>::parse_json_response_str(self, json_str)?.erase()
80    }
81
82    fn parse_json_response_value(
83        &self,
84        json_value: serde_json::Value,
85    ) -> Result<JsonRpcResponseErased, serde_json::Error> {
86        JsonRpcMethod::<I, O, E>::parse_json_response_value(self, json_value)?.erase()
87    }
88}
89
90impl<I, O, E> JsonRpcMethod<I, O, E>
91where
92    I: serde::de::DeserializeOwned + Serialize + 'static,
93    O: serde::de::DeserializeOwned + Serialize + 'static,
94    E: serde::de::DeserializeOwned + Serialize + 'static + MapErrorCode,
95{
96    pub fn erase_box(self) -> Box<dyn JsonRpcMethodErased> {
97        Box::new(self)
98    }
99
100    pub fn ref_erase(&self) -> &dyn JsonRpcMethodErased {
101        self
102    }
103}
104
105// The trait JsonRpcUnerased is only intended to be used by library developers
106//
107// The user of this library might want to use the strongly typed generic version
108// or the fully type-erased version
109//
110// As a library developer, we don't want to implement the same functionality twice
111// for the same RPC-call.
112//
113// That is why we introduce the JsonRpcUnerased trait.
114// It fills in the serde_json::Value type wherever either I, O or E should be.
115//
116// By using this trait, functionality will work for both type of users
117
118pub trait JsonRpcMethodUnerased<'a, I, O, E> {
119    fn name(&self) -> &str;
120
121    fn create_request(
122        &self,
123        params: I,
124        json_rpc_id: String,
125    ) -> Result<JsonRpcRequest<I>, serde_json::Error>;
126
127    fn parse_json_response_str(
128        &self,
129        json_str: &str,
130    ) -> Result<JsonRpcResponse<O, E>, serde_json::Error>;
131
132    fn parse_json_response_value(
133        &self,
134        json_value: serde_json::Value,
135    ) -> Result<JsonRpcResponse<O, E>, serde_json::Error>;
136}
137
138// Dummy implementation for when the user uses the generic api
139impl<'a, I, O, E> JsonRpcMethodUnerased<'a, I, O, E> for JsonRpcMethod<I, O, E>
140where
141    O: serde::de::DeserializeOwned,
142    E: serde::de::DeserializeOwned + MapErrorCode,
143{
144    fn name(&self) -> &str {
145        JsonRpcMethod::name(self)
146    }
147
148    fn create_request(
149        &self,
150        params: I,
151        json_rpc_id: String,
152    ) -> Result<JsonRpcRequest<I>, serde_json::Error> {
153        Ok(JsonRpcMethod::create_request(self, params, json_rpc_id))
154    }
155
156    fn parse_json_response_str(
157        &self,
158        json_str: &str,
159    ) -> Result<JsonRpcResponse<O, E>, serde_json::Error> {
160        JsonRpcMethod::parse_json_response_str(self, json_str)
161    }
162
163    fn parse_json_response_value(
164        &self,
165        json_value: serde_json::Value,
166    ) -> Result<JsonRpcResponse<O, E>, serde_json::Error> {
167        JsonRpcMethod::parse_json_response_value(self, json_value)
168    }
169}
170
171struct UneraseWrapper<'a> {
172    inner: &'a dyn JsonRpcMethodErased,
173}
174
175impl<'a> JsonRpcMethodUnerased<'a, Vec<u8>, Vec<u8>, Vec<u8>> for UneraseWrapper<'a> {
176    fn name(&self) -> &str {
177        self.inner.name()
178    }
179
180    fn create_request(
181        &self,
182        params: Vec<u8>,
183        json_rpc_id: String,
184    ) -> Result<JsonRpcRequestErased, serde_json::Error> {
185        self.inner.create_request(params, json_rpc_id)
186    }
187
188    fn parse_json_response_str(
189        &self,
190        json_str: &str,
191    ) -> Result<JsonRpcResponseErased, serde_json::Error> {
192        self.inner.parse_json_response_str(json_str)
193    }
194
195    fn parse_json_response_value(
196        &self,
197        json_value: serde_json::Value,
198    ) -> Result<JsonRpcResponseErased, serde_json::Error> {
199        self.inner.parse_json_response_value(json_value)
200    }
201}
202
203impl dyn JsonRpcMethodErased {
204    // The impl promises here we return a concrete type
205    // However, we'd rather keep the implementation details private in this module and don't want users messing with it
206    pub fn unerase(&self) -> impl JsonRpcMethodUnerased<Vec<u8>, Vec<u8>, Vec<u8>> {
207        UneraseWrapper { inner: self }
208    }
209}
210
211impl<I> JsonRpcRequest<I>
212where
213    I: Serialize,
214{
215    fn erase(self) -> Result<JsonRpcRequestErased, serde_json::Error> {
216        let value = serde_json::to_vec(&self.params)?;
217        Ok(JsonRpcRequest {
218            jsonrpc: self.jsonrpc,
219            id: self.id,
220            method: self.method,
221            params: value,
222        })
223    }
224}
225
226impl<O> JsonRpcResponseSuccess<O>
227where
228    O: Serialize,
229{
230    fn erase(self) -> Result<JsonRpcResponseSuccessErased, serde_json::Error> {
231        Ok(JsonRpcResponseSuccessErased {
232            id: self.id,
233            result: serde_json::to_vec(&self.result)?,
234            jsonrpc: self.jsonrpc,
235        })
236    }
237}
238
239impl<E> JsonRpcResponseFailure<E>
240where
241    E: Serialize,
242{
243    fn erase(self) -> Result<JsonRpcResponseFailureErased, serde_json::Error> {
244        Ok(JsonRpcResponseFailureErased {
245            id: self.id,
246            error: self.error.erase()?,
247            jsonrpc: self.jsonrpc,
248        })
249    }
250}
251
252impl<E> ErrorData<E>
253where
254    E: Serialize,
255{
256    fn erase(self) -> Result<JsonRpcErrorDataErased, serde_json::Error> {
257        let error_data = if let Some(error) = &self.data {
258            Some(serde_json::to_vec(error)?)
259        } else {
260            None
261        };
262
263        let x = JsonRpcErrorDataErased {
264            code: self.code,
265            data: error_data,
266            message: self.message,
267        };
268
269        Ok(x)
270    }
271}
272
273impl<O, E> JsonRpcResponse<O, E>
274where
275    O: Serialize,
276    E: Serialize,
277{
278    fn erase(self) -> Result<JsonRpcResponseErased, serde_json::Error> {
279        let result = match self {
280            Self::Ok(ok) => JsonRpcResponseErased::Ok(ok.erase()?),
281            Self::Error(err) => JsonRpcResponseErased::Error(err.erase()?),
282        };
283
284        Ok(result)
285    }
286}
287
288#[cfg(test)]
289mod test {
290    use super::*;
291    use crate::lsps::json_rpc::{generate_random_rpc_id, DefaultError, JsonRpcMethod};
292
293    #[derive(Serialize, serde::Deserialize)]
294    struct TestRequestStruct {
295        test: String,
296    }
297
298    #[derive(Serialize, serde::Deserialize)]
299    struct TestResponseStruct {
300        response: String,
301    }
302
303    #[test]
304    fn create_rpc_request_from_method_erased() {
305        let rpc_method = JsonRpcMethod::<TestRequestStruct, (), DefaultError>::new("test.method");
306        let rpc_method_erased = rpc_method.erase_box();
307
308        // This rpc-request should work becasue the parameters match the schema
309        let json_data = serde_json::json!({"test" : "This should work"});
310        let vec_data: Vec<u8> = serde_json::to_vec(&json_data).unwrap();
311
312        let json_rpc_id = generate_random_rpc_id();
313        let rpc_request: JsonRpcRequest<Vec<u8>> = rpc_method_erased
314            .create_request(vec_data, json_rpc_id)
315            .unwrap();
316        assert_eq!(rpc_request.method, "test.method");
317    }
318
319    #[test]
320    fn create_rpc_request_from_method_erased_checks_types() {
321        let rpc_method = JsonRpcMethod::<TestRequestStruct, (), DefaultError>::new("test.method");
322        let rpc_method_erased = rpc_method.erase_box();
323
324        // This rpc-request should fail because the parameters do not match the schema
325        // The test field is missing
326        let param_vec = serde_json::to_vec(&serde_json::json!({})).unwrap();
327        let json_rpc_id = generate_random_rpc_id();
328        let rpc_request = rpc_method_erased.create_request(param_vec, json_rpc_id);
329        assert!(rpc_request.is_err());
330    }
331
332    #[test]
333    fn parse_rpc_request_from_method_erased() {
334        let rpc_method = JsonRpcMethod::<TestRequestStruct, TestResponseStruct, DefaultError>::new(
335            "test.method",
336        );
337        let rpc_method_erased = rpc_method.erase_box();
338
339        let json_value = serde_json::json!({
340            "jsonrpc" : "2.0",
341            "id" : "abcdef",
342            "result" : {"response" : "content"}
343        });
344
345        rpc_method_erased
346            .parse_json_response_value(json_value)
347            .unwrap();
348    }
349
350    #[test]
351    fn parse_rpc_request_from_method_erased_fails() {
352        let rpc_method = JsonRpcMethod::<TestRequestStruct, TestResponseStruct, DefaultError>::new(
353            "test.method",
354        );
355        let rpc_method_erased = rpc_method.erase_box();
356
357        let json_value = serde_json::json!({
358            "jsonrpd" : "2.0", // See the typo-here
359            "id" : "abcdef",
360            "result" : {"response" : "content"}
361        });
362
363        let result: Result<JsonRpcResponseErased, serde_json::Error> =
364            rpc_method_erased.parse_json_response_value(json_value);
365        assert!(result.is_err());
366
367        // TODO: improve the error-message here
368        // It currently gives a vague error-message about not matching one of the enum scenarios in JsonRpcResponse
369        // It should at least mention that the field jsonrpc is missing
370        //assert!(format!("{:?}", result).contains("jsonrpc"));
371    }
372}