jsonrpc_derive/
lib.rs

1//! High level, typed wrapper for `jsonrpc_core`.
2//!
3//! Enables creation of "Service" objects grouping a set of RPC methods together in a typed manner.
4//!
5//! Example
6//!
7//! ```
8//! use jsonrpc_core::{IoHandler, Result, BoxFuture};
9//! use jsonrpc_core::futures::future;
10//! use jsonrpc_derive::rpc;
11//!
12//! #[rpc(server)]
13//! pub trait Rpc {
14//!     #[rpc(name = "protocolVersion")]
15//!     fn protocol_version(&self) -> Result<String>;
16//!
17//!     #[rpc(name = "add")]
18//!     fn add(&self, a: u64, b: u64) -> Result<u64>;
19//!
20//!     #[rpc(name = "callAsync")]
21//!     fn call(&self, a: u64) -> BoxFuture<Result<String>>;
22//! }
23//!
24//! struct RpcImpl;
25//! impl Rpc for RpcImpl {
26//!     fn protocol_version(&self) -> Result<String> {
27//!         Ok("version1".into())
28//!     }
29//!
30//!     fn add(&self, a: u64, b: u64) -> Result<u64> {
31//!         Ok(a + b)
32//!     }
33//!
34//!     fn call(&self, _: u64) -> BoxFuture<Result<String>> {
35//!         Box::pin(future::ready(Ok("OK".to_owned()).into()))
36//!     }
37//! }
38//!
39//! fn main() {
40//!   let mut io = IoHandler::new();
41//!   let rpc = RpcImpl;
42//!
43//!   io.extend_with(rpc.to_delegate());
44//! }
45//! ```
46//!
47//! Pub/Sub Example
48//!
49//! Each subscription must have `subscribe` and `unsubscribe` methods. They can
50//! have any name but must be annotated with `subscribe` or `unsubscribe` and
51//! have a matching unique subscription name.
52//!
53//! ```
54//! use std::sync::{atomic, Arc, RwLock};
55//! use std::collections::HashMap;
56//!
57//! use jsonrpc_core::{Error, ErrorCode, Result};
58//! use jsonrpc_derive::rpc;
59//! use jsonrpc_pubsub::{Session, PubSubHandler, SubscriptionId, typed::{Subscriber, Sink}};
60//!
61//! #[rpc]
62//! pub trait Rpc {
63//!    type Metadata;
64//!
65//!    /// Hello subscription
66//!    #[pubsub(
67//!        subscription = "hello",
68//!        subscribe,
69//!        name = "hello_subscribe",
70//!        alias("hello_sub")
71//!     )]
72//!     fn subscribe(&self, _: Self::Metadata, _: Subscriber<String>, param: u64);
73//!
74//!     /// Unsubscribe from hello subscription.
75//!     #[pubsub(
76//!         subscription = "hello",
77//!         unsubscribe,
78//!         name = "hello_unsubscribe"
79//!     )]
80//!     fn unsubscribe(&self, _: Option<Self::Metadata>, _: SubscriptionId) -> Result<bool>;
81//! }
82//!
83//!
84//! #[derive(Default)]
85//! struct RpcImpl {
86//!     uid: atomic::AtomicUsize,
87//!     active: Arc<RwLock<HashMap<SubscriptionId, Sink<String>>>>,
88//! }
89//! impl Rpc for RpcImpl {
90//!     type Metadata = Arc<Session>;
91//!
92//!     fn subscribe(&self, _meta: Self::Metadata, subscriber: Subscriber<String>, param: u64) {
93//!         if param != 10 {
94//!             subscriber.reject(Error {
95//!                  code: ErrorCode::InvalidParams,
96//!                  message: "Rejecting subscription - invalid parameters provided.".into(),
97//!                  data: None,
98//!             }).unwrap();
99//!             return;
100//!         }
101//!
102//!         let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
103//!         let sub_id = SubscriptionId::Number(id as u64);
104//!         let sink = subscriber.assign_id(sub_id.clone()).unwrap();
105//!         self.active.write().unwrap().insert(sub_id, sink);
106//!     }
107//!
108//!     fn unsubscribe(&self, _meta: Option<Self::Metadata>, id: SubscriptionId) -> Result<bool> {
109//!          let removed = self.active.write().unwrap().remove(&id);
110//!          if removed.is_some() {
111//!              Ok(true)
112//!          } else {
113//!              Err(Error {
114//!                  code: ErrorCode::InvalidParams,
115//!                  message: "Invalid subscription.".into(),
116//!                  data: None,
117//!          })
118//!         }
119//!     }
120//! }
121//!
122//! fn main() {
123//!   let mut io = jsonrpc_core::MetaIoHandler::default();
124//!   io.extend_with(RpcImpl::default().to_delegate());
125//!
126//!   let server_builder = jsonrpc_tcp_server::ServerBuilder::with_meta_extractor(
127//!     io,
128//!     |request: &jsonrpc_tcp_server::RequestContext| Arc::new(Session::new(request.sender.clone()))
129//!   );
130//!   let server = server_builder
131//!     .start(&"127.0.0.1:3030".parse().unwrap())
132//!     .expect("Unable to start TCP server");
133//!
134//!   // The server spawns a separate thread. Dropping the `server` handle causes it to close.
135//!   // Uncomment the line below to keep the server running in your example.
136//!   // server.wait();
137//! }
138//! ```
139//!
140//! Client Example
141//!
142//! ```
143//! use jsonrpc_core_client::transports::local;
144//! use jsonrpc_core::futures::{self, future};
145//! use jsonrpc_core::{IoHandler, Result, BoxFuture};
146//! use jsonrpc_derive::rpc;
147//!
148//! /// Rpc trait
149//! #[rpc]
150//! pub trait Rpc {
151//!     /// Returns a protocol version
152//!     #[rpc(name = "protocolVersion")]
153//!     fn protocol_version(&self) -> Result<String>;
154//!
155//!     /// Adds two numbers and returns a result
156//!     #[rpc(name = "add", alias("callAsyncMetaAlias"))]
157//!     fn add(&self, a: u64, b: u64) -> Result<u64>;
158//!
159//!     /// Performs asynchronous operation
160//!     #[rpc(name = "callAsync")]
161//!     fn call(&self, a: u64) -> BoxFuture<Result<String>>;
162//! }
163//!
164//! struct RpcImpl;
165//!
166//! impl Rpc for RpcImpl {
167//!    fn protocol_version(&self) -> Result<String> {
168//!         Ok("version1".into())
169//!    }
170//!
171//!    fn add(&self, a: u64, b: u64) -> Result<u64> {
172//!         Ok(a + b)
173//!    }
174//!
175//!    fn call(&self, _: u64) -> BoxFuture<Result<String>> {
176//!         Box::pin(future::ready(Ok("OK".to_owned())))
177//!    }
178//! }
179//!
180//! fn main() {
181//!     let exec = futures::executor::ThreadPool::new().unwrap();
182//!     exec.spawn_ok(run())
183//! }
184//! async fn run() {
185//!    let mut io = IoHandler::new();
186//!    io.extend_with(RpcImpl.to_delegate());
187//!
188//!    let (client, server) = local::connect::<RpcClient, _, _>(io);
189//!    let res = client.add(5, 6).await.unwrap();
190//!    println!("5 + 6 = {}", res);
191//!    server.await.unwrap()
192//! }
193//!
194//! ```
195
196#![recursion_limit = "256"]
197#![warn(missing_docs)]
198
199extern crate proc_macro;
200
201use proc_macro::TokenStream;
202use syn::parse_macro_input;
203
204mod options;
205mod params_style;
206mod rpc_attr;
207mod rpc_trait;
208mod to_client;
209mod to_delegate;
210
211/// Apply `#[rpc]` to a trait, and a `to_delegate` method is generated which
212/// wires up methods decorated with `#[rpc]` or `#[pubsub]` attributes.
213/// Attach the delegate to an `IoHandler` and the methods are now callable
214/// via JSON-RPC.
215#[proc_macro_attribute]
216pub fn rpc(args: TokenStream, input: TokenStream) -> TokenStream {
217	let input_toks = parse_macro_input!(input as syn::Item);
218	let args = syn::parse_macro_input!(args as syn::AttributeArgs);
219
220	let options = match options::DeriveOptions::try_from(args) {
221		Ok(options) => options,
222		Err(error) => return error.to_compile_error().into(),
223	};
224
225	match rpc_trait::rpc_impl(input_toks, &options) {
226		Ok(output) => output.into(),
227		Err(err) => err.to_compile_error().into(),
228	}
229}