diff --git a/tarpc/src/macros.rs b/tarpc/src/macros.rs index 02868bc..8a4dd0c 100644 --- a/tarpc/src/macros.rs +++ b/tarpc/src/macros.rs @@ -10,6 +10,70 @@ #[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)] @@ -70,6 +134,7 @@ macro_rules! request_variant { #[macro_export] macro_rules! rpc { ( + $(#[$($service_attr:tt)*])* mod $server:ident { service { @@ -81,6 +146,7 @@ macro_rules! rpc { } ) => { rpc! { + $(#[$($service_attr)*])* mod $server { items { } @@ -97,6 +163,7 @@ macro_rules! rpc { ( // Names the service + $(#[$($service_attr:tt)*])* mod $server:ident { // Include any desired or required items. Conflicts can arise with the following names: @@ -116,77 +183,80 @@ macro_rules! rpc { } } ) => { - #[doc="A module containing an rpc service and client stub."] - pub mod $server { - $($i)* + add_mod_doc! { + { $(#[$($service_attr)*])* } + pub mod $server { - #[doc="The provided RPC service."] - pub trait Service: Send + Sync { - $( - $(#[$attr])* - fn $fn_name(&self, $($arg:$in_),*) -> $out; - )* - } + $($i)* - define_request!($($fn_name($($in_),*))*); - - #[allow(non_camel_case_types)] - #[derive(Debug, Serialize, Deserialize)] - enum __Reply { - $( - $fn_name($out), - )* - } - - #[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)) + #[doc="The provided RPC service."] + pub trait Service: Send + Sync { + $( + $(#[$attr])* + fn $fn_name(&self, $($arg:$in_),*) -> $out; + )* } - client_methods!( + define_request!($($fn_name($($in_),*))*); + + #[allow(non_camel_case_types)] + #[derive(Debug, Serialize, Deserialize)] + enum __Reply { $( - { $(#[$attr])* } - $fn_name($($arg: $in_),*) -> $out + $fn_name($out), )* - ); - } + } - struct __Server(S); + #[doc="The client stub that makes RPC calls to the server."] + pub struct Client($crate::protocol::Client<__Request, __Reply>); - impl $crate::protocol::Serve for __Server - where S: 'static + Service - { - type Request = __Request; - type Reply = __Reply; - fn serve(&self, request: __Request) -> __Reply { - match request { + 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!( $( - request_variant!($fn_name $($arg),*) => - __Reply::$fn_name((self.0).$fn_name($($arg),*)), - )* + { $(#[$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))) + } } } } @@ -202,6 +272,8 @@ mod test { } rpc! { + #[deny(missing_docs)] + #[doc="Hello"] mod my_server { items { #[derive(PartialEq, Debug, Serialize, Deserialize)] @@ -245,7 +317,7 @@ mod test { // Tests a service definition with a fn that takes no args rpc! { - mod foo { + mod qux { service { rpc hello() -> String; } @@ -254,6 +326,22 @@ mod test { // Tests a service definition with an import rpc! { + mod foo { + items { + use std::collections::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; @@ -261,8 +349,35 @@ mod test { service { #[doc="Hello bob"] + #[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; + + #[derive(Debug)] + pub struct Debuggable; + } + + service { + #[doc="Hello bob"] + #[inline(always)] + rpc baz(s: String) -> HashMap; + } + } + } + + #[test] + fn debug() { + println!("{:?}", baz::Debuggable); + } }