Add first step of async-trait

This commit is contained in:
Lucio Franco
2019-09-06 01:30:51 -04:00
parent b569280d41
commit 295e7321b9
7 changed files with 190 additions and 236 deletions
+1 -1
View File
@@ -4,5 +4,5 @@ members = [
"tonic-macros",
"tonic-build",
"tonic-examples",
"tonic-interop",
# "tonic-interop",
]
+3 -3
View File
@@ -14,9 +14,9 @@ path = "src/helloworld/server.rs"
name = "helloworld-client"
path = "src/helloworld/client.rs"
[[bin]]
name = "routeguide-server"
path = "src/routeguide/server.rs"
# [[bin]]
# name = "routeguide-server"
# path = "src/routeguide/server.rs"
[[bin]]
name = "routeguide-client"
+6 -9
View File
@@ -1,8 +1,8 @@
use tonic::transport::Server;
use tonic::{Request, Response, Status};
use tonic::{Request, Response, Status, Server};
pub mod hello_world {
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
tonic::server!(service = "helloworld.Greeter", proto = "self");
}
#[derive(Default, Clone)]
@@ -10,12 +10,9 @@ pub struct MyGreeter {
data: String,
}
#[tonic::server(service = "helloworld.Greeter", proto = "hello_world")]
impl MyGreeter {
pub async fn say_hello(
&self,
request: Request<hello_world::HelloRequest>,
) -> Result<Response<hello_world::HelloReply>, Status> {
#[tonic::server_trait]
impl hello_world::Greeter for MyGreeter {
async fn say_hello(self, request: Request<hello_world::HelloRequest>) -> Result<Response<hello_world::HelloReply>, Status> {
println!("Got a request: {:?}", request);
let string = &self.data;
@@ -35,7 +32,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let greeter = MyGreeter::default();
Server::builder()
.serve(addr, GreeterServer::new(greeter))
.serve(addr, hello_world::GreeterServer::new(greeter))
.await?;
Ok(())
+4 -8
View File
@@ -57,19 +57,15 @@ pub fn client(attr: TokenStream) -> TokenStream {
TokenStream::from(output)
}
#[proc_macro_attribute]
pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut original = item.clone();
let item = syn::parse_macro_input!(item as ItemImpl);
#[proc_macro]
pub fn server(attr: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(attr as AttributeArgs);
let (service, proto_path) = load_service(args);
let service_def = service::parse_service_impl(item, service, proto_path);
let output = service::generate(service_def);
let output = service::generate(service, &proto_path);
original.extend(TokenStream::from(output));
original
TokenStream::from(output)
}
fn load_service(attr: AttributeArgs) -> (Service, String) {
+169 -215
View File
@@ -1,99 +1,49 @@
use crate::{Method, Service};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, LitStr, Path, Type};
use syn::{Ident, Lit, LitStr, Path};
#[derive(Debug)]
pub struct ServiceDef {
name: Path,
name_str: String,
package: String,
proto_name: String,
proto_path: String,
methods: Vec<(Method, Ident)>,
}
pub(crate) fn generate(service: Service, proto_path: &str) -> TokenStream {
let methods = generate_methods(&service, proto_path);
pub(crate) fn parse_service_impl(
item: ItemImpl,
mut service: Service,
proto_path: String,
) -> ServiceDef {
let ItemImpl { self_ty, items, .. } = item;
let name = if let Type::Path(t) = *self_ty {
t.path.clone()
} else {
panic!("wrong type!")
};
let mut methods = Vec::new();
for item in items {
if let ImplItem::Method(method) = item {
let ImplItemMethod { sig, .. } = method;
if sig.asyncness.is_some() {
let name = format!("{}", sig.ident);
if let Some((i, _)) = service
.methods
.iter()
.enumerate()
.find(|(_, method)| method.name == name)
{
let method = service.methods.remove(i);
methods.push((method, sig.ident));
}
}
}
}
ServiceDef {
name,
name_str: service.name,
package: service.package,
proto_name: service.proto_name,
proto_path,
methods,
}
}
pub(crate) fn generate(service: ServiceDef) -> TokenStream {
let service_impl = service.name.clone();
let methods = generate_methods(&service);
let server_make_service = quote::format_ident!("{}Server", service.name_str);
let server_service = quote::format_ident!("{}ServerSvc", service.name_str);
let server_make_service = quote::format_ident!("{}Server", service.name);
let server_service = quote::format_ident!("{}ServerSvc", service.name);
let server_trait = quote::format_ident!("{}", service.name);
quote! {
use tonic::_codegen::*;
#[async_trait]
pub trait #server_trait : Clone + Send + 'static {
async fn say_hello(self, req: tonic::Request<self::HelloRequest>)
-> Result<tonic::Response<self::HelloReply>, tonic::Status>;
}
// TODO: impl debug
#[derive(Clone)]
pub struct #server_make_service {
inner: std::sync::Arc<#service_impl>,
pub struct #server_make_service <T: #server_trait > {
inner: T,
}
// TODO: impl debug
pub struct #server_service {
inner: std::sync::Arc<#service_impl>,
pub struct #server_service <T: #server_trait > {
inner: T,
}
impl #server_make_service {
pub fn new(t: #service_impl) -> Self {
let inner = std::sync::Arc::new(t);
impl<T: #server_trait > #server_make_service <T> {
pub fn new(inner: T) -> Self {
Self { inner }
}
}
impl #server_service {
pub fn new(inner: std::sync::Arc<#service_impl>) -> Self {
impl<T: #server_trait > #server_service <T> {
pub fn new(inner: T) -> Self {
Self { inner }
}
}
impl<T> Service<T> for #server_make_service {
type Response = #server_service;
impl<T: #server_trait , R> Service<R> for #server_make_service <T> {
type Response = #server_service <T>;
type Error = tonic::error::Never;
type Future = Ready<Result<Self::Response, Self::Error>>;
@@ -101,12 +51,12 @@ pub(crate) fn generate(service: ServiceDef) -> TokenStream {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: T) -> Self::Future {
fn call(&mut self, _: R) -> Self::Future {
ok(#server_service ::new(self.inner.clone()))
}
}
impl Service<http::Request<tonic::_codegen::HyperBody>> for #server_service {
impl<T: #server_trait > Service<http::Request<tonic::_codegen::HyperBody>> for #server_service <T> {
type Response = http::Response<tonic::BoxBody>;
type Error = tonic::error::Never;
type Future = BoxFuture<Self::Response, Self::Error>;
@@ -128,44 +78,48 @@ pub(crate) fn generate(service: ServiceDef) -> TokenStream {
}
}
fn generate_methods(service: &ServiceDef) -> TokenStream {
fn generate_methods(service: &Service, proto_path: &str) -> TokenStream {
let mut stream = TokenStream::new();
for (method, ident) in &service.methods {
for method in &service.methods {
let path = format!(
"/{}.{}/{}",
service.package, service.proto_name, method.proto_name
);
let method_path = Lit::Str(LitStr::new(&path, Span::call_site()));
let ident = quote::format_ident!("{}", method.name);
let server_trait = quote::format_ident!("{}", service.name);
let method_stream = match (method.client_streaming, method.server_streaming) {
(false, false) => generate_unary(
method,
ident.clone(),
service.name.clone(),
&service.proto_path,
ident,
proto_path,
server_trait
),
(false, true) => generate_server_streaming(
method,
ident.clone(),
service.name.clone(),
&service.proto_path,
),
_ => unimplemented!()
(true, false) => generate_client_streaming(
method,
ident.clone(),
service.name.clone(),
&service.proto_path,
),
// (false, true) => generate_server_streaming(
// method,
// ident.clone(),
// service.name.clone(),
// &service.proto_path,
// ),
(true, true) => generate_streaming(
method,
ident.clone(),
service.name.clone(),
&service.proto_path,
),
// (true, false) => generate_client_streaming(
// method,
// ident.clone(),
// service.name.clone(),
// &service.proto_path,
// ),
// (true, true) => generate_streaming(
// method,
// ident.clone(),
// service.name.clone(),
// &service.proto_path,
// ),
};
let method = quote! {
@@ -182,8 +136,8 @@ fn generate_methods(service: &ServiceDef) -> TokenStream {
fn generate_unary(
method: &Method,
method_ident: Ident,
service_impl: Path,
proto_path: &str,
server_trait: Ident,
) -> TokenStream {
let service_ident = Ident::new(&method.proto_name, Span::call_site());
@@ -192,9 +146,9 @@ fn generate_unary(
syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
quote! {
struct #service_ident(pub std::sync::Arc<#service_impl>);
struct #service_ident <T: #server_trait >(pub T);
impl tonic::server::UnaryService<#request> for #service_ident {
impl<T: #server_trait > tonic::server::UnaryService<#request> for #service_ident <T> {
type Response = #response;
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
@@ -221,141 +175,141 @@ fn generate_unary(
}
}
fn generate_server_streaming(
method: &Method,
method_ident: Ident,
service_impl: Path,
proto_path: &str,
) -> TokenStream {
let service_ident = Ident::new(&method.proto_name, Span::call_site());
// fn generate_server_streaming(
// method: &Method,
// method_ident: Ident,
// service_impl: Path,
// proto_path: &str,
// ) -> TokenStream {
// let service_ident = Ident::new(&method.proto_name, Span::call_site());
let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
let response: Path =
syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
// let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
// let response: Path =
// syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
// TODO: parse response stream type, if it is a concrete type then use that
// as the ResponseStream type, if it is a impl Trait then we need to box.
quote! {
struct #service_ident(pub std::sync::Arc<#service_impl>);
// // TODO: parse response stream type, if it is a concrete type then use that
// // as the ResponseStream type, if it is a impl Trait then we need to box.
// quote! {
// struct #service_ident(pub std::sync::Arc<#service_impl>);
impl tonic::server::ServerStreamingService<#request> for #service_ident {
type Response = #response;
type ResponseStream = Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>;
type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
// impl tonic::server::ServerStreamingService<#request> for #service_ident {
// type Response = #response;
// type ResponseStream = Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>;
// type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
fn call(&mut self, request: tonic::Request<#request>) -> Self::Future {
let inner = self.0.clone();
let fut = async move {
inner.#method_ident(request)
.await
.map(|r|
r.map(|s| Box::pin(s) as Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>))
// fn call(&mut self, request: tonic::Request<#request>) -> Self::Future {
// let inner = self.0.clone();
// let fut = async move {
// inner.#method_ident(request)
// .await
// .map(|r|
// r.map(|s| Box::pin(s) as Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>))
};
Box::pin(fut)
}
}
// };
// Box::pin(fut)
// }
// }
let inner = self.inner.clone();
let fut = async move {
let method = #service_ident(inner);
let codec = tonic::codec::ProstCodec::new();
let mut grpc = tonic::server::Grpc::new(codec);
let res = grpc.server_streaming(method, req).await;
Ok(res)
};
// let inner = self.inner.clone();
// let fut = async move {
// let method = #service_ident(inner);
// let codec = tonic::codec::ProstCodec::new();
// let mut grpc = tonic::server::Grpc::new(codec);
// let res = grpc.server_streaming(method, req).await;
// Ok(res)
// };
Box::pin(fut)
}
}
// Box::pin(fut)
// }
// }
fn generate_client_streaming(
method: &Method,
method_ident: Ident,
service_impl: Path,
proto_path: &str,
) -> TokenStream {
let service_ident = Ident::new(&method.proto_name, Span::call_site());
// fn generate_client_streaming(
// method: &Method,
// method_ident: Ident,
// service_impl: Path,
// proto_path: &str,
// ) -> TokenStream {
// let service_ident = Ident::new(&method.proto_name, Span::call_site());
let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
let response: Path =
syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
// let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
// let response: Path =
// syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
quote! {
struct #service_ident(pub std::sync::Arc<#service_impl>);
// quote! {
// struct #service_ident(pub std::sync::Arc<#service_impl>);
impl<S> tonic::server::ClientStreamingService<S> for #service_ident
where S: tonic::_codegen::Stream<Item = Result<#request, Status>> + Unpin + Send + 'static {
type Response = #response;
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
// impl<S> tonic::server::ClientStreamingService<S> for #service_ident
// where S: tonic::_codegen::Stream<Item = Result<#request, Status>> + Unpin + Send + 'static {
// type Response = #response;
// type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
fn call(&mut self, request: tonic::Request<S>) -> Self::Future {
let inner = self.0.clone();
let fut = async move {
inner.#method_ident(request).await
// fn call(&mut self, request: tonic::Request<S>) -> Self::Future {
// let inner = self.0.clone();
// let fut = async move {
// inner.#method_ident(request).await
};
Box::pin(fut)
}
}
// };
// Box::pin(fut)
// }
// }
let inner = self.inner.clone();
let fut = async move {
let method = #service_ident(inner);
let codec = tonic::codec::ProstCodec::new();
let mut grpc = tonic::server::Grpc::new(codec);
let res = grpc.client_streaming(method, req).await;
Ok(res)
};
// let inner = self.inner.clone();
// let fut = async move {
// let method = #service_ident(inner);
// let codec = tonic::codec::ProstCodec::new();
// let mut grpc = tonic::server::Grpc::new(codec);
// let res = grpc.client_streaming(method, req).await;
// Ok(res)
// };
Box::pin(fut)
}
}
// Box::pin(fut)
// }
// }
fn generate_streaming(
method: &Method,
method_ident: Ident,
service_impl: Path,
proto_path: &str,
) -> TokenStream {
let service_ident = Ident::new(&method.proto_name, Span::call_site());
// fn generate_streaming(
// method: &Method,
// method_ident: Ident,
// service_impl: Path,
// proto_path: &str,
// ) -> TokenStream {
// let service_ident = Ident::new(&method.proto_name, Span::call_site());
let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
let response: Path =
syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
// let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
// let response: Path =
// syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
// TODO: parse response stream type, if it is a concrete type then use that
// as the ResponseStream type, if it is a impl Trait then we need to box.
quote! {
struct #service_ident(pub std::sync::Arc<#service_impl>);
// // TODO: parse response stream type, if it is a concrete type then use that
// // as the ResponseStream type, if it is a impl Trait then we need to box.
// quote! {
// struct #service_ident(pub std::sync::Arc<#service_impl>);
impl<S> tonic::server::StreamingService<S> for #service_ident
where S: Stream<Item = Result<#request, Status>> + Unpin + Send + 'static {
type Response = #response;
type ResponseStream = Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>;
type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
// impl<S> tonic::server::StreamingService<S> for #service_ident
// where S: Stream<Item = Result<#request, Status>> + Unpin + Send + 'static {
// type Response = #response;
// type ResponseStream = Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>;
// type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
fn call(&mut self, request: tonic::Request<S>) -> Self::Future {
let inner = self.0.clone();
let fut = async move {
inner.#method_ident(request).await
.map(|r|
r.map(|s| Box::pin(s) as Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>))
// fn call(&mut self, request: tonic::Request<S>) -> Self::Future {
// let inner = self.0.clone();
// let fut = async move {
// inner.#method_ident(request).await
// .map(|r|
// r.map(|s| Box::pin(s) as Pin<Box<dyn Stream<Item = Result<Self::Response, Status>> + Send>>))
};
Box::pin(fut)
}
}
// };
// Box::pin(fut)
// }
// }
let inner = self.inner.clone();
let fut = async move {
let method = #service_ident(inner);
let codec = tonic::codec::ProstCodec::new();
let mut grpc = tonic::server::Grpc::new(codec);
let res = grpc.streaming(method, req).await;
Ok(res)
};
// let inner = self.inner.clone();
// let fut = async move {
// let method = #service_ident(inner);
// let codec = tonic::codec::ProstCodec::new();
// let mut grpc = tonic::server::Grpc::new(codec);
// let res = grpc.streaming(method, req).await;
// Ok(res)
// };
Box::pin(fut)
}
}
// Box::pin(fut)
// }
// }
+1
View File
@@ -19,6 +19,7 @@ tokio-codec = "=0.2.0-alpha.4"
async-stream = { git = "https://github.com/tokio-rs/async-stream" }
http-body = "0.2.0-alpha.1"
pin-project = "=0.4.0-alpha.7"
async-trait = "0.1"
# optional
hyper = { version = "=0.13.0-alpha.1", optional = true }
+6
View File
@@ -51,11 +51,17 @@ pub use request::Request;
pub use response::Response;
pub use status::{Code, Status};
pub use tonic_macros::{client, server};
#[doc(inline)]
pub use transport::{Channel, Server};
pub(crate) use error::Error;
#[doc(hidden)]
pub use async_trait::async_trait as server_trait;
#[doc(hidden)]
pub mod _codegen {
pub use async_trait::async_trait;
pub use futures_core::Stream;
pub use futures_util::future::{ok, poll_fn, Ready};
pub use http_body::Body as HttpBody;