susydev_jsonrpc_derive/
lib.rs

1//! High level, typed wrapper for `susydev_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 susydev_jsonrpc_derive::rpc;
9//! use susydev_jsonrpc_core::{IoHandler, Error, Result};
10//! use susydev_jsonrpc_core::futures::future::{self, FutureResult};
11//!
12//! #[rpc]
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) -> FutureResult<String, Error>;
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) -> FutureResult<String, Error> {
35//! 		future::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::thread;
55//! use std::sync::{atomic, Arc, RwLock};
56//! use std::collections::HashMap;
57//!
58//! use susydev_jsonrpc_core::{Error, ErrorCode, Result};
59//! use susydev_jsonrpc_core::futures::Future;
60//! use susydev_jsonrpc_derive::rpc;
61//! use susydev_jsonrpc_pubsub::{Session, PubSubHandler, SubscriptionId, typed::{Subscriber, Sink}};
62//!
63//! #[rpc]
64//! pub trait Rpc {
65//!		type Metadata;
66//!
67//!		/// Hello subscription
68//!		#[pubsub(
69//! 		subscription = "hello",
70//! 		subscribe,
71//! 		name = "hello_subscribe",
72//! 		alias("hello_sub")
73//! 	)]
74//!		fn subscribe(&self, _: Self::Metadata, _: Subscriber<String>, _: u64);
75//!
76//!		/// Unsubscribe from hello subscription.
77//!		#[pubsub(
78//! 		subscription = "hello",
79//! 		unsubscribe,
80//! 		name = "hello_unsubscribe"
81//! 	)]
82//!		fn unsubscribe(&self, _: Option<Self::Metadata>, _: SubscriptionId) -> Result<bool>;
83//! }
84//!
85//!
86//! #[derive(Default)]
87//! struct RpcImpl {
88//! 	uid: atomic::AtomicUsize,
89//! 	active: Arc<RwLock<HashMap<SubscriptionId, Sink<String>>>>,
90//! }
91//! impl Rpc for RpcImpl {
92//! 	type Metadata = Arc<Session>;
93//!
94//! 	fn subscribe(&self, _meta: Self::Metadata, subscriber: Subscriber<String>, param: u64) {
95//! 		if param != 10 {
96//! 			subscriber.reject(Error {
97//! 				code: ErrorCode::InvalidParams,
98//! 				message: "Rejecting subscription - invalid parameters provided.".into(),
99//! 				data: None,
100//! 			}).unwrap();
101//! 			return;
102//! 		}
103//!
104//! 		let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
105//! 		let sub_id = SubscriptionId::Number(id as u64);
106//! 		let sink = subscriber.assign_id(sub_id.clone()).unwrap();
107//! 		self.active.write().unwrap().insert(sub_id, sink);
108//! 	}
109//!
110//! 	fn unsubscribe(&self, _meta: Option<Self::Metadata>, id: SubscriptionId) -> Result<bool> {
111//! 		let removed = self.active.write().unwrap().remove(&id);
112//! 		if removed.is_some() {
113//! 			Ok(true)
114//! 		} else {
115//! 			Err(Error {
116//! 				code: ErrorCode::InvalidParams,
117//! 				message: "Invalid subscription.".into(),
118//! 				data: None,
119//! 			})
120//! 		}
121//! 	}
122//! }
123//!
124//! # fn main() {}
125//! ```
126//! 
127//! Client Example
128//! 
129//! ```
130//! use susydev_jsonrpc_client::local;
131//! use susydev_jsonrpc_core::futures::future::{self, Future, FutureResult};
132//! use susydev_jsonrpc_core::{Error, IoHandler, Result};
133//! use susydev_jsonrpc_derive::rpc;
134//!
135//! /// Rpc trait
136//! #[rpc]
137//! pub trait Rpc {
138//! 	/// Returns a protocol version
139//! 	#[rpc(name = "protocolVersion")]
140//! 	fn protocol_version(&self) -> Result<String>;
141//!
142//! 	/// Adds two numbers and returns a result
143//! 	#[rpc(name = "add", alias("callAsyncMetaAlias"))]
144//! 	fn add(&self, a: u64, b: u64) -> Result<u64>;
145//!
146//! 	/// Performs asynchronous operation
147//! 	#[rpc(name = "callAsync")]
148//! 	fn call(&self, a: u64) -> FutureResult<String, Error>;
149//! }
150//!
151//! struct RpcImpl;
152//!
153//! impl Rpc for RpcImpl {
154//! 	fn protocol_version(&self) -> Result<String> {
155//! 		Ok("version1".into())
156//! 	}
157//!
158//! 	fn add(&self, a: u64, b: u64) -> Result<u64> {
159//! 		Ok(a + b)
160//! 	}
161//!
162//! 	fn call(&self, _: u64) -> FutureResult<String, Error> {
163//! 		future::ok("OK".to_owned())
164//! 	}
165//! }
166//!
167//! fn main() {
168//! 	let mut io = IoHandler::new();
169//! 	io.extend_with(RpcImpl.to_delegate());
170//!
171//! 	let fut = {
172//! 		let (client, server) = local::connect::<gen_client::Client, _, _>(io);
173//! 		client.add(5, 6).map(|res| println!("5 + 6 = {}", res)).join(server)
174//! 	};
175//! 	fut.wait().unwrap();
176//! }
177//! 
178//! ```
179
180#![recursion_limit = "256"]
181#![warn(missing_docs)]
182
183extern crate proc_macro;
184
185use proc_macro::TokenStream;
186use syn::parse_macro_input;
187
188mod options;
189mod rpc_attr;
190mod rpc_trait;
191mod to_client;
192mod to_delegate;
193
194/// Apply `#[rpc]` to a trait, and a `to_delegate` method is generated which
195/// wires up methods decorated with `#[rpc]` or `#[pubsub]` attributes.
196/// Attach the delegate to an `IoHandler` and the methods are now callable
197/// via JSON-RPC.
198#[proc_macro_attribute]
199pub fn rpc(args: TokenStream, input: TokenStream) -> TokenStream {
200	let input_toks = parse_macro_input!(input as syn::Item);
201
202	let options = match options::DeriveOptions::try_from(args) {
203		Ok(options) => options,
204		Err(error) => return error.to_compile_error().into(),
205	};
206
207	match rpc_trait::rpc_impl(input_toks, options) {
208		Ok(output) => output.into(),
209		Err(err) => err.to_compile_error().into(),
210	}
211}