Add first step of async-trait
This commit is contained in:
@@ -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
@@ -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)
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user