futures_jsonrpc/lib.rs
1//! Futures + JSON-RPC
2//!
3//! A lightweight remote procedure call protocol. It is designed to be simple! And, with futures, even more flexible!
4//!
5//! This crate will associate [Future](futures::future::Future)s with method signatures via [register_method](handler::JrpcHandler::register_method), and parse/handle JSON-RPC messages via [handle_message](handler::JrpcHandler::handle_message).
6//!
7//! It is fully compliant with [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification).
8//!
9//! ## Installation
10//!
11//! Add this to your `Cargo.toml`:
12//!
13//! ```toml
14//! [dependencies]
15//! futures_jsonrpc = "0.2"
16//! ```
17//!
18//! ## Minimal example
19//!
20//! ```
21//! use futures_jsonrpc::futures::prelude::*;
22//! use futures_jsonrpc::*;
23//! use serde_json::Number;
24//!
25//! // This macro will avoid some boilerplating, leaving only the `Future` implementation to be done
26//! //
27//! // Check for additional information in the detailed explanation below
28//! //
29//! // Also, check `generate_method_with_data_and_future` and `generate_method_with_lifetime_data_and_future`
30//! generate_method!(
31//! CopyParams,
32//! impl Future for CopyParams {
33//! type Item = Option<JrpcResponse>;
34//! type Error = ErrorVariant;
35//!
36//! fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
37//! let request = self.get_request()?;
38//! let params = request.get_params().clone().unwrap_or(JsonValue::Null);
39//!
40//! let message = JrpcResponseParam::generate_result(params)
41//! .and_then(|result| request.generate_response(result))?;
42//!
43//! Ok(Async::Ready(Some(message)))
44//! }
45//! }
46//! );
47//!
48//! fn main() {
49//! // `JrpcHandler` instance is responsible for registering the JSON-RPC methods and receiving the
50//! // requests.
51//! //
52//! // This is full `Arc`/`RwLock` protected. Therefore, it can be freely copied/sent among
53//! // threads.
54//! let handler = JrpcHandler::new().unwrap();
55//!
56//! handler
57//! // `register_method` will tie the method signature to an instance, not a generic. This
58//! // means we can freely mutate this instance across different signatures.
59//! .register_method("some/copyParams", CopyParams::new().unwrap())
60//!
61//! .and_then(|h| {
62//! // `handle_message` will receive a raw implementation of `ToString` and return the
63//! // associated future. If no future is found, an instance of
64//! // `Err(ErrorVariant::MethodSignatureNotFound(String))` is returned
65//! h.handle_message(
66//! r#"
67//! {
68//! "jsonrpc": "2.0",
69//! "method": "some/copyParams",
70//! "params": [42, 23],
71//! "id": 531
72//! }"#,
73//! )
74//! })
75//!
76//! // Just waiting for the poll of future. Check futures documentation.
77//! .and_then(|future| future.wait())
78//! .and_then(|result| {
79//! // The result is an instance of `JrpcResponse`
80//! let result = result.unwrap();
81//!
82//! assert_eq!(result.get_jsonrpc(), "2.0");
83//! assert_eq!(
84//! result.get_result(),
85//! &Some(JsonValue::Array(vec![
86//! JsonValue::Number(Number::from(42)),
87//! JsonValue::Number(Number::from(23)),
88//! ]))
89//! );
90//! assert!(result.get_error().is_none());
91//! assert_eq!(result.get_id(), &JsonValue::Number(Number::from(531)));
92//! Ok(())
93//! })
94//! .unwrap();
95//! }
96//! ```
97//!
98//! ## Detailed explanation
99//!
100//! ```
101//! use futures_jsonrpc::futures::prelude::*;
102//! use futures_jsonrpc::*;
103//! use std::marker::PhantomData;
104//!
105//! // `JrpcHandler` use foreign structures as controllers
106//! // This example will reflect `generate_method_with_lifetime_data_and_future` macro
107//! #[derive(Debug, Clone)]
108//! pub struct CopyParams<'r> {
109//! request: Option<JrpcRequest>,
110//! data: (String, i32, PhantomData<&'r ()>),
111//! }
112//!
113//! // This implementation is essentially some boilerplate to hold the data that may be used by the
114//! // future poll
115//! impl<'r> CopyParams<'r> {
116//! // The `new` method will always receive a n-tuple as parameter to store data
117//! //
118//! // It is recommended to use atomic types, or `Arc` protected for heavy data. At every request,
119//! // we `Clone` this struct to send it to the responsible thread
120//! pub fn new(data: (String, i32, PhantomData<&'r ()>)) -> Result<Self, ErrorVariant> {
121//! let request = None;
122//! let some_notification = CopyParams { request, data };
123//! Ok(some_notification)
124//! }
125//!
126//! // The `get_data` will support the future poll with additional information that will not be
127//! // available in the JsonRpc request
128//! pub fn get_data(&self) -> &(String, i32, PhantomData<&'r ()>) {
129//! &self.data
130//! }
131//!
132//! // The `get_request` method will return the JsonRpc request to the future poll
133//! pub fn get_request(&self) -> Result<JrpcRequest, ErrorVariant> {
134//! let request = self.request.clone();
135//! request
136//! .map(|r| Ok(r.clone()))
137//! .unwrap_or(Err(ErrorVariant::NoRequestProvided))
138//! }
139//!
140//! // This method is of internal usage to receive the request from `JrpcHandler`
141//! pub fn set_request(mut self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
142//! self.request = Some(request);
143//! Ok(self)
144//! }
145//!
146//! // This "fork" will be performed every time a new request is received, allowing async
147//! // processing
148//! pub fn clone_with_request(&self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
149//! self.clone().set_request(request)
150//! }
151//! }
152//!
153//! // `JrpcHandler` will just return a pollable associated future.
154//! //
155//! // The main implementation will go here
156//! //
157//! // Tokio provides very good documentation on futures. Check it: https://tokio.rs/
158//! impl<'r> Future for CopyParams<'r> {
159//! // Optimally, we want to use JrpcResponse, for it is guaranteed to respect the JSON-RPC
160//! // specification. But, we can change the response here to something else, if required.
161//! type Item = Option<JrpcResponse>;
162//! type Error = ErrorVariant;
163//!
164//! fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
165//! // We fetch the provided request to copy the data
166//! let request = self.get_request()?;
167//!
168//! // Here we can receive additional that that's not available in the request
169//! let (_text, _value, _) = self.get_data();
170//!
171//! // Do something with the request
172//! // In this example, we are copying the parameters
173//! let params = request.get_params().clone().unwrap_or(JsonValue::Null);
174//!
175//! // `generate_response` will receive an enum `JrpcResponseParam` and reply
176//! // with either an error or success.
177//! let message = JrpcResponseParam::generate_result(params)
178//! .and_then(|result| request.generate_response(result))?;
179//!
180//! // Then, our reply is ready
181//! Ok(Async::Ready(Some(message)))
182//! }
183//! }
184//!
185//! // The handler will call this trait to spawn a new future and process it when a registered method
186//! // is requested.
187//! impl<'r> JrpcMethodTrait<'r> for CopyParams<'r> {
188//! // `generate_future` can generate any `Future` that respects the trait signature. This can be a
189//! // foreign structure, or just a copy of `self`, in case it implements `Future`. This can also
190//! // be a decision based on the received `JrpcRequest`.
191//! //
192//! // Since its not a reference, there are no restrictions.
193//! fn generate_future(
194//! &self,
195//! request: JrpcRequest,
196//! ) -> Result<Box<'r + Future<Item = Option<JrpcResponse>, Error = ErrorVariant>>, ErrorVariant>
197//! {
198//! Ok(Box::new(self.clone_with_request(request)?))
199//! }
200//! }
201//! ```
202
203#[macro_use]
204extern crate log;
205
206pub use crate::handler::JrpcHandler;
207pub use crate::method::JrpcMethodTrait;
208pub use crate::parser::{JrpcError, JrpcErrorEnum, JrpcRequest, JrpcResponse, JrpcResponseParam};
209pub use futures;
210pub use serde_json::error::Error as JsonError;
211pub use serde_json::Value as JsonValue;
212use std::fmt;
213use std::io::Error as IoError;
214
215pub mod handler;
216pub mod method;
217pub mod parser;
218
219#[derive(Debug)]
220pub enum ErrorVariant {
221 RwLockPoisoned,
222 MethodSignatureNotFound(String),
223 JsonParseError(JsonError),
224 InvalidJsonRpcVersion,
225 InvalidJsonRpcId,
226 ResponseCannotContainResultAndError,
227 ResponseMustContainResultOrError,
228 NoRequestProvided,
229 IoError(IoError),
230 InternalError,
231 InternalErrorMessage(String),
232}
233
234impl fmt::Display for ErrorVariant {
235 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 match self {
237 ErrorVariant::MethodSignatureNotFound(s) => {
238 write!(f, "Method signature '{}' not found", s)
239 }
240 ErrorVariant::InternalErrorMessage(s) => write!(f, "An error ocurred: {}", s),
241 _ => write!(f, "{:?}", self),
242 }
243 }
244}
245
246#[macro_export]
247macro_rules! generate_method {
248 ($struct_identifier:ident, $future:item) => {
249 #[derive(Debug, Clone)]
250 pub struct $struct_identifier {
251 request: Option<JrpcRequest>,
252 }
253
254 impl $struct_identifier {
255 pub fn new() -> Result<Self, ErrorVariant> {
256 let request = None;
257 let some_notification = $struct_identifier {
258 request,
259 };
260 Ok(some_notification)
261 }
262
263 pub fn get_request(&self) -> Result<JrpcRequest, ErrorVariant> {
264 let request = self.request.clone();
265 request
266 .map(|r| Ok(r.clone()))
267 .unwrap_or(Err(ErrorVariant::NoRequestProvided))
268 }
269
270 pub fn set_request(mut self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
271 self.request = Some(request);
272 Ok(self)
273 }
274
275 pub fn clone_with_request(&self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
276 self.clone().set_request(request)
277 }
278 }
279
280 $future
281
282 impl<'r> JrpcMethodTrait<'r> for $struct_identifier {
283 fn generate_future(
284 &self,
285 request: JrpcRequest,
286 ) -> Result<
287 Box<'r + Future<Item = Option<JrpcResponse>, Error = ErrorVariant>>,
288 ErrorVariant,
289 > {
290 Ok(Box::new(self.clone_with_request(request)?))
291 }
292 }
293 };
294}
295
296#[macro_export]
297macro_rules! generate_method_with_data_and_future {
298 ($struct_identifier:ident, $data:ty, $future:item) => {
299 #[derive(Debug, Clone)]
300 pub struct $struct_identifier {
301 request: Option<JrpcRequest>,
302 data: $data,
303 }
304
305 impl $struct_identifier {
306 pub fn new(data: $data) -> Result<Self, ErrorVariant> {
307 let request = None;
308 let some_notification = $struct_identifier {
309 request,
310 data,
311 };
312 Ok(some_notification)
313 }
314
315 pub fn get_data(&self) -> &$data {
316 &self.data
317 }
318
319 pub fn get_request(&self) -> Result<JrpcRequest, ErrorVariant> {
320 let request = self.request.clone();
321 request
322 .map(|r| Ok(r.clone()))
323 .unwrap_or(Err(ErrorVariant::NoRequestProvided))
324 }
325
326 pub fn set_request(mut self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
327 self.request = Some(request);
328 Ok(self)
329 }
330
331 pub fn clone_with_request(&self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
332 self.clone().set_request(request)
333 }
334 }
335
336 $future
337
338 impl<'r> JrpcMethodTrait<'r> for $struct_identifier {
339 fn generate_future(
340 &self,
341 request: JrpcRequest,
342 ) -> Result<
343 Box<'r + Future<Item = Option<JrpcResponse>, Error = ErrorVariant>>,
344 ErrorVariant,
345 > {
346 Ok(Box::new(self.clone_with_request(request)?))
347 }
348 }
349 };
350}
351
352#[macro_export]
353macro_rules! generate_method_with_lifetime_data_and_future {
354 ($struct_identifier:ident, $lifetime:tt, $data:ty, $future:item) => {
355 #[derive(Debug, Clone)]
356 pub struct $struct_identifier<$lifetime> {
357 request: Option<JrpcRequest>,
358 data: $data,
359 }
360
361 impl<$lifetime> $struct_identifier<$lifetime> {
362 pub fn new(data: $data) -> Result<Self, ErrorVariant> {
363 let request = None;
364 let some_notification = $struct_identifier {
365 request,
366 data,
367 };
368 Ok(some_notification)
369 }
370
371 pub fn get_data(&self) -> &$data {
372 &self.data
373 }
374
375 pub fn get_request(&self) -> Result<JrpcRequest, ErrorVariant> {
376 let request = self.request.clone();
377 request
378 .map(|r| Ok(r.clone()))
379 .unwrap_or(Err(ErrorVariant::NoRequestProvided))
380 }
381
382 pub fn set_request(mut self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
383 self.request = Some(request);
384 Ok(self)
385 }
386
387 pub fn clone_with_request(&self, request: JrpcRequest) -> Result<Self, ErrorVariant> {
388 self.clone().set_request(request)
389 }
390 }
391
392 $future
393
394 impl<$lifetime> JrpcMethodTrait<$lifetime> for $struct_identifier<$lifetime> {
395 fn generate_future(
396 &self,
397 request: JrpcRequest,
398 ) -> Result<
399 Box<$lifetime + Future<Item = Option<JrpcResponse>, Error = ErrorVariant>>,
400 ErrorVariant,
401 > {
402 Ok(Box::new(self.clone_with_request(request)?))
403 }
404 }
405 };
406}