diff --git a/README.md b/README.md index 1384825..b68d6fd 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,9 @@ tarpc is an RPC framework for rust with a focus on ease of use. Defining and imp extern crate tarpc; extern crate serde; -rpc! { - mod hello_service { - service { - rpc hello(name: String) -> String; - } +mod hello_service { + service! { + rpc hello(name: String) -> String; } } @@ -33,11 +31,18 @@ fn main() { } ``` -The `rpc!` macro generates a module in the current module. In the above example, the module is named `hello_service`. This module will contain a `Client` type, a `Service` trait, and a `serve` function. `serve` can be used to start a server listening on a tcp port. A `Client` can connect to such a service. Any type implementing the `Service` trait can be passed to `serve`. These generated types are specific to the echo service, and make it easy and ergonomic to write servers without dealing with sockets or serialization directly. See the tarpc_examples package for more sophisticated examples. +The `service!` macro expands to a collection of items that collectively form an rpc service. In the +above example, the macro is called within the `hello_service` module. This module will contain a +`Client` type, a `Service` trait, and a `serve` function. `serve` can be used to start a server +listening on a tcp port. A `Client` can connect to such a service. Any type implementing the +`Service` trait can be passed to `serve`. These generated types are specific to the echo service, +and make it easy and ergonomic to write servers without dealing with sockets or serialization +directly. See the tarpc_examples package for more sophisticated examples. ## Additional Features - Imports can be specified in an `item {}` block that appears above the `service {}` block. -- Attributes can be specified on rpc methods. These will be included on both the `Service` trait methods as well as on the `Client`'s stub methods. +- Attributes can be specified on rpc methods. These will be included on both the `Service` trait + methods as well as on the `Client`'s stub methods. ## Planned Improvements (actively being worked on) - Automatically reconnect on the client side when the connection cuts out. diff --git a/tarpc/src/lib.rs b/tarpc/src/lib.rs index 2f6755b..e597ca9 100644 --- a/tarpc/src/lib.rs +++ b/tarpc/src/lib.rs @@ -15,12 +15,10 @@ //! # #![plugin(serde_macros)] //! # #[macro_use] extern crate tarpc; //! # extern crate serde; -//! rpc! { -//! mod my_server { -//! service { -//! rpc hello(name: String) -> String; -//! rpc add(x: i32, y: i32) -> i32; -//! } +//! mod my_server { +//! service! { +//! rpc hello(name: String) -> String; +//! rpc add(x: i32, y: i32) -> i32; //! } //! } //! diff --git a/tarpc/src/macros.rs b/tarpc/src/macros.rs index b5195f1..a0fd95b 100644 --- a/tarpc/src/macros.rs +++ b/tarpc/src/macros.rs @@ -10,70 +10,6 @@ #[macro_export] macro_rules! as_item { ($i:item) => {$i} } -// Inserts a placeholder doc comment for the module if it's missing -#[doc(hidden)] -#[macro_export] -macro_rules! add_mod_doc { - // If nothing left, return - ( - @rec - { $(#[$done:meta])* } - { } - $i:item - ) => { - $(#[$done])* - #[doc="A module containing an rpc service and client stub."] - $i - }; - - // If we find a doc attribute, return - ( - @rec - { $(#[$done:meta])* } - { - #[doc=$doc:expr] - $(#[$rest:meta])* - } - $i:item - ) => { - $(#[$done])* - #[doc=$doc] - $(#[$rest])* - $i - }; - - // If we don't find a doc attribute, keep going - ( - @rec - { $(#[$($done:tt)*])* } - { - #[$($attr:tt)*] - $($rest:tt)* - } - $i:item - ) => { - add_mod_doc! { - @rec - { $(#[$($done)*])* #[$($attr)*] } - { $($rest)* } - $i - } - }; - - // Entry - ( - { $(#[$($attr:tt)*])* } - $i:item - ) => { - add_mod_doc! { - @rec - {} - { $(#[$($attr)*])* } - $i - } - }; -} - // Required because if-let can't be used with irrefutable patterns, so it needs // to be special cased. #[doc(hidden)] @@ -176,178 +112,126 @@ macro_rules! request_variant { // The main macro that creates RPC services. #[macro_export] -macro_rules! rpc { +macro_rules! service { ( - $(#[$($service_attr:tt)*])* - mod $server:ident { - - service { - $( - $(#[$attr:meta])* - rpc $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty; - )* - } - } + // List any rpc methods: rpc foo(arg1: Arg1, ..., argN: ArgN) -> Out + $( + $(#[$attr:meta])* + rpc $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty; + )* ) => { - rpc! { - $(#[$($service_attr)*])* - mod $server { + #[doc="The provided RPC service."] + pub trait Service: Send + Sync { + $( + $(#[$attr])* + fn $fn_name(&self, $($arg:$in_),*) -> $out; + )* + } - items { } - - service { - $( - $(#[$attr])* - rpc $fn_name($($arg: $in_),*) -> $out; - )* + impl Service for P + where P: Send + Sync + ::std::ops::Deref, + S: Service + { + $( + $(#[$attr])* + fn $fn_name(&self, $($arg:$in_),*) -> $out { + Service::$fn_name(&**self, $($arg),*) } + )* + } + + define_request!($($fn_name($($in_),*))*); + + #[allow(non_camel_case_types)] + #[derive(Debug, Serialize, Deserialize)] + enum __Reply { + $( + $fn_name($out), + )* + } + + /// An asynchronous RPC call + pub struct Future { + future: $crate::protocol::Future<__Reply>, + mapper: fn(__Reply) -> T, + } + + impl Future { + /// Block until the result of the RPC call is available + pub fn get(self) -> $crate::Result { + self.future.get().map(self.mapper) } } - }; - ( - // Names the service - $(#[$($service_attr:tt)*])* - mod $server:ident { + #[doc="The client stub that makes RPC calls to the server."] + pub struct Client($crate::protocol::Client<__Request, __Reply>); - // Include any desired or required items. Conflicts can arise with the following names: - // 1. Service - // 2. Client - // 3. serve - // 4. __Reply - // 5. __Request - items { $($i:item)* } + impl Client { + #[doc="Create a new client that connects to the given address."] + pub fn new(addr: A, timeout: ::std::option::Option<::std::time::Duration>) + -> $crate::Result + where A: ::std::net::ToSocketAddrs, + { + let inner = try!($crate::protocol::Client::new(addr, timeout)); + Ok(Client(inner)) + } - // List any rpc methods: rpc foo(arg1: Arg1, ..., argN: ArgN) -> Out - service { + client_methods!( $( - $(#[$attr:meta])* - rpc $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty; + { $(#[$attr])* } + $fn_name($($arg: $in_),*) -> $out )* + ); + } + + #[doc="The client stub that makes asynchronous RPC calls to the server."] + pub struct AsyncClient($crate::protocol::Client<__Request, __Reply>); + + impl AsyncClient { + #[doc="Create a new asynchronous client that connects to the given address."] + pub fn new(addr: A, timeout: ::std::option::Option<::std::time::Duration>) + -> $crate::Result + where A: ::std::net::ToSocketAddrs, + { + let inner = try!($crate::protocol::Client::new(addr, timeout)); + Ok(AsyncClient(inner)) + } + + async_client_methods!( + $( + { $(#[$attr])* } + $fn_name($($arg: $in_),*) -> $out + )* + ); + } + + struct __Server(S); + + impl $crate::protocol::Serve for __Server + where S: 'static + Service + { + type Request = __Request; + type Reply = __Reply; + fn serve(&self, request: __Request) -> __Reply { + match request { + $( + request_variant!($fn_name $($arg),*) => + __Reply::$fn_name((self.0).$fn_name($($arg),*)), + )* + } } } - ) => { - add_mod_doc! { - { $(#[$($service_attr)*])* } - pub mod $server { - - $($i)* - - #[doc="The provided RPC service."] - pub trait Service: Send + Sync { - $( - $(#[$attr])* - fn $fn_name(&self, $($arg:$in_),*) -> $out; - )* - } - - impl Service for P - where P: Send + Sync + ::std::ops::Deref, - S: Service - { - $( - $(#[$attr])* - fn $fn_name(&self, $($arg:$in_),*) -> $out { - Service::$fn_name(&**self, $($arg),*) - } - )* - } - - define_request!($($fn_name($($in_),*))*); - - #[allow(non_camel_case_types)] - #[derive(Debug, Serialize, Deserialize)] - enum __Reply { - $( - $fn_name($out), - )* - } - - /// An asynchronous RPC call - pub struct Future { - future: $crate::protocol::Future<__Reply>, - mapper: fn(__Reply) -> T, - } - - impl Future { - /// Block until the result of the RPC call is available - pub fn get(self) -> $crate::Result { - self.future.get().map(self.mapper) - } - } - - #[doc="The client stub that makes RPC calls to the server."] - pub struct Client($crate::protocol::Client<__Request, __Reply>); - - impl Client { - #[doc="Create a new client that connects to the given address."] - pub fn new(addr: A, timeout: ::std::option::Option<::std::time::Duration>) - -> $crate::Result - where A: ::std::net::ToSocketAddrs, - { - let inner = try!($crate::protocol::Client::new(addr, timeout)); - Ok(Client(inner)) - } - - client_methods!( - $( - { $(#[$attr])* } - $fn_name($($arg: $in_),*) -> $out - )* - ); - } - - #[doc="The client stub that makes asynchronous RPC calls to the server."] - pub struct AsyncClient($crate::protocol::Client<__Request, __Reply>); - - impl AsyncClient { - #[doc="Create a new asynchronous client that connects to the given address."] - pub fn new(addr: A, timeout: ::std::option::Option<::std::time::Duration>) - -> $crate::Result - where A: ::std::net::ToSocketAddrs, - { - let inner = try!($crate::protocol::Client::new(addr, timeout)); - Ok(AsyncClient(inner)) - } - - async_client_methods!( - $( - { $(#[$attr])* } - $fn_name($($arg: $in_),*) -> $out - )* - ); - } - - struct __Server(S); - - impl $crate::protocol::Serve for __Server - where S: 'static + Service - { - type Request = __Request; - type Reply = __Reply; - fn serve(&self, request: __Request) -> __Reply { - match request { - $( - request_variant!($fn_name $($arg),*) => - __Reply::$fn_name((self.0).$fn_name($($arg),*)), - )* - } - } - } - - #[doc="Start a running service."] - pub fn serve(addr: A, - service: S, - read_timeout: ::std::option::Option<::std::time::Duration>) - -> $crate::Result<$crate::protocol::ServeHandle> - where A: ::std::net::ToSocketAddrs, - S: 'static + Service - { - let server = ::std::sync::Arc::new(__Server(service)); - Ok(try!($crate::protocol::serve_async(addr, server, read_timeout))) - } - } + #[doc="Start a running service."] + pub fn serve(addr: A, + service: S, + read_timeout: ::std::option::Option<::std::time::Duration>) + -> $crate::Result<$crate::protocol::ServeHandle> + where A: ::std::net::ToSocketAddrs, + S: 'static + Service + { + let server = ::std::sync::Arc::new(__Server(service)); + Ok(try!($crate::protocol::serve_async(addr, server, read_timeout))) } } } @@ -363,28 +247,23 @@ mod test { Some(Duration::from_secs(5)) } - rpc! { - #[deny(missing_docs)] - #[doc="Hello"] - mod my_server { - items { - #[derive(PartialEq, Debug, Serialize, Deserialize)] - pub struct Foo { - pub message: String - } - } + #[derive(PartialEq, Debug, Serialize, Deserialize)] + pub struct Foo { + pub message: String + } - service { - rpc hello(foo: Foo) -> Foo; - rpc add(x: i32, y: i32) -> i32; - } + mod my_server { + use super::Foo; + + service! { + rpc hello(foo: Foo) -> Foo; + rpc add(x: i32, y: i32) -> i32; } } - use self::my_server::*; - struct Server; - impl Service for Server { + + impl my_server::Service for Server { fn hello(&self, s: Foo) -> Foo { Foo { message: format!("Hello, {}", &s.message) } } @@ -396,7 +275,7 @@ mod test { #[test] fn serve_arc_server() { - serve("localhost:0", ::std::sync::Arc::new(Server), None) + my_server::serve("localhost:0", ::std::sync::Arc::new(Server), None) .unwrap() .shutdown(); } @@ -404,7 +283,7 @@ mod test { #[test] fn simple() { let handle = my_server::serve( "localhost:0", Server, test_timeout()).unwrap(); - let client = Client::new(handle.local_addr(), None).unwrap(); + let client = my_server::Client::new(handle.local_addr(), None).unwrap(); assert_eq!(3, client.add(1, 2).unwrap()); let foo = Foo { message: "Adam".into() }; let want = Foo { message: format!("Hello, {}", &foo.message) }; @@ -416,7 +295,7 @@ mod test { #[test] fn simple_async() { let handle = my_server::serve("localhost:0", Server, test_timeout()).unwrap(); - let client = AsyncClient::new(handle.local_addr(), None).unwrap(); + let client = my_server::AsyncClient::new(handle.local_addr(), None).unwrap(); assert_eq!(3, client.add(1, 2).get().unwrap()); let foo = Foo { message: "Adam".into() }; let want = Foo { message: format!("Hello, {}", &foo.message) }; @@ -425,63 +304,48 @@ mod test { handle.shutdown(); } - // Tests a service definition with a fn that takes no args - rpc! { - mod qux { - service { - rpc hello() -> String; - } + /// Tests a service definition with a fn that takes no args + mod qux { + service! { + rpc hello() -> String; } } - // Tests a service definition with an import - rpc! { - mod foo { - items { - use std::collections::HashMap; - } + /// Tests a service definition with an import + mod foo { + use std::collections::HashMap; - service { - #[doc="Hello bob"] - #[inline(always)] - rpc baz(s: String) -> HashMap; - } + service! { + #[doc="Hello bob"] + #[inline(always)] + rpc baz(s: String) -> HashMap; } } - // Tests a service definition with an attribute but no doc comment - rpc! { - #[deny(missing_docs)] - mod bar { - items { - use std::collections::HashMap; - } + /// Tests a service definition with an attribute but no doc comment + #[deny(missing_docs)] + mod bar { + use std::collections::HashMap; - service { - #[inline(always)] - rpc baz(s: String) -> HashMap; - } + service! { + #[inline(always)] + rpc baz(s: String) -> HashMap; } } - // Tests a service definition with an attribute and a doc comment - rpc! { - #[deny(missing_docs)] - #[doc="Hello bob"] - #[allow(unused)] - mod baz { - items { - use std::collections::HashMap; + /// Tests a service definition with an attribute and a doc comment + #[deny(missing_docs)] + #[allow(unused)] + mod baz { + use std::collections::HashMap; - #[derive(Debug)] - pub struct Debuggable; - } + #[derive(Debug)] + pub struct Debuggable; - service { - #[doc="Hello bob"] - #[inline(always)] - rpc baz(s: String) -> HashMap; - } + service! { + #[doc="Hello bob"] + #[inline(always)] + rpc baz(s: String) -> HashMap; } } @@ -490,16 +354,15 @@ mod test { println!("{:?}", baz::Debuggable); } - rpc! { - mod hello { - service { - rpc hello(s: String) -> String; - } + mod hi { + service! { + rpc hello(s: String) -> String; } } struct HelloServer; - impl hello::Service for HelloServer { + + impl hi::Service for HelloServer { fn hello(&self, s: String) -> String { format!("Hello, {}!", s) } @@ -508,8 +371,8 @@ mod test { #[bench] fn hello(bencher: &mut Bencher) { let _ = env_logger::init(); - let handle = hello::serve("localhost:0", HelloServer, None).unwrap(); - let client = hello::AsyncClient::new(handle.local_addr(), None).unwrap(); + let handle = hi::serve("localhost:0", HelloServer, None).unwrap(); + let client = hi::AsyncClient::new(handle.local_addr(), None).unwrap(); let concurrency = 100; let mut rpcs = Vec::with_capacity(concurrency); bencher.iter(|| {