First pass at macro
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
members = [
|
members = [
|
||||||
"tonic",
|
"tonic",
|
||||||
"tonic-macros",
|
"tonic-macros",
|
||||||
|
"tonic-build",
|
||||||
"tonic-examples",
|
"tonic-examples",
|
||||||
"tower-h2"
|
"tower-h2"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "tonic-build"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Lucio Franco <[email protected]>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
prost-build = "0.5"
|
||||||
|
codegen = "0.1"
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
use prost_build::Config;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::{io, path};
|
||||||
|
|
||||||
|
pub fn compile_protos<P>(protos: &[P], includes: &[P]) -> io::Result<()>
|
||||||
|
where
|
||||||
|
P: AsRef<path::Path>,
|
||||||
|
{
|
||||||
|
let mut config = Config::new();
|
||||||
|
|
||||||
|
config.service_generator(Box::new(ServiceGenerator {}));
|
||||||
|
|
||||||
|
config.compile_protos(protos, includes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ServiceGenerator {}
|
||||||
|
|
||||||
|
impl prost_build::ServiceGenerator for ServiceGenerator {
|
||||||
|
fn generate(&mut self, service: prost_build::Service, _buf: &mut String) {
|
||||||
|
let file = format!(
|
||||||
|
"{}/{}.{}.json",
|
||||||
|
std::env::var("OUT_DIR").unwrap(),
|
||||||
|
service.package,
|
||||||
|
service.name
|
||||||
|
);
|
||||||
|
|
||||||
|
let svc = Service {
|
||||||
|
name: service.name,
|
||||||
|
proto_name: service.proto_name,
|
||||||
|
package: service.package,
|
||||||
|
methods: service
|
||||||
|
.methods
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| Method {
|
||||||
|
name: m.name,
|
||||||
|
proto_name: m.proto_name,
|
||||||
|
input_type: m.input_type,
|
||||||
|
output_type: m.output_type,
|
||||||
|
input_proto_type: m.input_proto_type,
|
||||||
|
output_proto_type: m.output_proto_type,
|
||||||
|
client_streaming: m.client_streaming,
|
||||||
|
server_streaming: m.server_streaming,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&svc).unwrap();
|
||||||
|
|
||||||
|
std::fs::write(file, json).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn finalize(&mut self, buf: &mut String) {
|
||||||
|
// let mut fmt = codegen::Formatter::new(buf);
|
||||||
|
// self.scope
|
||||||
|
// .fmt(&mut fmt)
|
||||||
|
// .expect("formatting root scope failed!");
|
||||||
|
// self.scope = codegen::Scope::new();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A service descriptor.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Service {
|
||||||
|
/// The service name in Rust style.
|
||||||
|
pub name: String,
|
||||||
|
/// The service name as it appears in the .proto file.
|
||||||
|
pub proto_name: String,
|
||||||
|
/// The package name as it appears in the .proto file.
|
||||||
|
pub package: String,
|
||||||
|
/// The service methods.
|
||||||
|
pub methods: Vec<Method>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A service method descriptor.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Method {
|
||||||
|
/// The name of the method in Rust style.
|
||||||
|
pub name: String,
|
||||||
|
/// The name of the method as it appears in the .proto file.
|
||||||
|
pub proto_name: String,
|
||||||
|
/// The input Rust type.
|
||||||
|
pub input_type: String,
|
||||||
|
/// The output Rust type.
|
||||||
|
pub output_type: String,
|
||||||
|
/// The input Protobuf type.
|
||||||
|
pub input_proto_type: String,
|
||||||
|
/// The output Protobuf type.
|
||||||
|
pub output_proto_type: String,
|
||||||
|
// /// The method options.
|
||||||
|
// pub options: prost_types::MethodOptions,
|
||||||
|
/// Identifies if client streams multiple client messages.
|
||||||
|
pub client_streaming: bool,
|
||||||
|
/// Identifies if server streams multiple server messages.
|
||||||
|
pub server_streaming: bool,
|
||||||
|
}
|
||||||
@@ -22,3 +22,6 @@ tokio = "=0.2.0-alpha.1"
|
|||||||
prost = "0.5"
|
prost = "0.5"
|
||||||
prost-derive = "0.5"
|
prost-derive = "0.5"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tonic-build = { path = "../tonic-build" }
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
fn main() {}
|
fn main() {
|
||||||
|
tonic_build::compile_protos(
|
||||||
|
&["proto/helloworld/helloworld.proto"],
|
||||||
|
&["proto/helloworld"],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,18 +5,8 @@ use tokio::{timer::Delay, net::TcpListener};
|
|||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
use tower_h2::Server;
|
use tower_h2::Server;
|
||||||
|
|
||||||
mod proto {
|
pub mod hello_world {
|
||||||
#[derive(Clone, PartialEq, prost::Message)]
|
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
||||||
pub struct HelloRequest {
|
|
||||||
#[prost(string, tag = "1")]
|
|
||||||
pub name: std::string::String,
|
|
||||||
}
|
|
||||||
/// The response message containing the greetings
|
|
||||||
#[derive(Clone, PartialEq, prost::Message)]
|
|
||||||
pub struct HelloReply {
|
|
||||||
#[prost(string, tag = "1")]
|
|
||||||
pub message: std::string::String,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@@ -24,9 +14,9 @@ pub struct MyGreeter {
|
|||||||
data: String,
|
data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tonic::server(service = "helloworld.Greeter", proto = "proto")]
|
#[tonic::server(service = "helloworld.Greeter", proto = "hello_world")]
|
||||||
impl MyGreeter {
|
impl MyGreeter {
|
||||||
pub async fn say_hello(&self, request: Request<proto::HelloRequest>) -> Result<Response<proto::HelloReply>, Status> {
|
pub async fn say_hello(&self, request: Request<hello_world::HelloRequest>) -> Result<Response<hello_world::HelloReply>, Status> {
|
||||||
println!("Got a request: {:?}", request);
|
println!("Got a request: {:?}", request);
|
||||||
|
|
||||||
let string = &self.data;
|
let string = &self.data;
|
||||||
@@ -38,7 +28,7 @@ impl MyGreeter {
|
|||||||
|
|
||||||
Delay::new(when).await;
|
Delay::new(when).await;
|
||||||
|
|
||||||
let reply = HelloReply {
|
let reply = hello_world::HelloReply {
|
||||||
message: "Zomg, it works!".into(),
|
message: "Zomg, it works!".into(),
|
||||||
};
|
};
|
||||||
Ok(Response::new(reply))
|
Ok(Response::new(reply))
|
||||||
@@ -51,7 +41,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let mut bind = TcpListener::bind(&addr)?;
|
let mut bind = TcpListener::bind(&addr)?;
|
||||||
|
|
||||||
let greeter = MyGreeter::default();
|
let greeter = MyGreeter::default();
|
||||||
let mut server = Server::new(GrpcServer::new(greeter), Default::default());
|
let mut server = Server::new(GreeterServer::new(greeter), Default::default());
|
||||||
|
|
||||||
while let Ok((sock, _addr)) = bind.accept().await {
|
while let Ok((sock, _addr)) = bind.accept().await {
|
||||||
if let Err(e) = sock.set_nodelay(true) {
|
if let Err(e) = sock.set_nodelay(true) {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ edition = "2018"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "0.15", features = ["full"] }
|
syn = { version = "1.0", features = ["full"] }
|
||||||
quote = "0.6"
|
quote = "1.0"
|
||||||
proc-macro2 = "0.4"
|
proc-macro2 = "1.0"
|
||||||
prost-build = "0.5"
|
serde_json = "1.0"
|
||||||
tower-service = { git = "https://github.com/tower-rs/tower", branch = "std-future" }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = "=0.2.0-alpha.1"
|
tokio = "=0.2.0-alpha.1"
|
||||||
|
|||||||
+168
-130
@@ -2,155 +2,193 @@
|
|||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
mod service;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use prost_build::{Comments, Method, Service};
|
use serde::Deserialize;
|
||||||
use quote::quote;
|
use syn::{AttributeArgs, ItemImpl};
|
||||||
use syn::{ImplItem, ImplItemMethod, ItemImpl, Type};
|
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let service = load_service(attr);
|
|
||||||
let mut original = item.clone();
|
let mut original = item.clone();
|
||||||
let ItemImpl { self_ty, items, .. } = syn::parse_macro_input!(item as ItemImpl);
|
let item = syn::parse_macro_input!(item as ItemImpl);
|
||||||
|
let args = syn::parse_macro_input!(attr as AttributeArgs);
|
||||||
|
|
||||||
let s = if let Type::Path(t) = *self_ty {
|
let service = load_service(args);
|
||||||
t.path.segments.iter().next().unwrap().clone()
|
let service_def = service::parse_service_impl(item, service);
|
||||||
} else {
|
let output = service::generate(service_def);
|
||||||
panic!("wrong type!")
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut m_ident = None;
|
original.extend(TokenStream::from(output));
|
||||||
for item in items {
|
original
|
||||||
if let ImplItem::Method(method) = item {
|
|
||||||
// println!("{:?}", method);
|
|
||||||
|
|
||||||
let ImplItemMethod { sig, .. } = method;
|
// let mut original = item.clone();
|
||||||
|
// let ItemImpl { self_ty, items, .. } = syn::parse_macro_input!(item as ItemImpl);
|
||||||
|
|
||||||
if sig.asyncness.is_some() {
|
// let mut m_ident = None;
|
||||||
let name = format!("{}", sig.ident);
|
// for item in items {
|
||||||
|
// if let ImplItem::Method(method) = item {
|
||||||
|
// // println!("{:?}", method);
|
||||||
|
|
||||||
if let Some(_method) = service.methods.iter().find(|method| method.name == name) {
|
// let ImplItemMethod { sig, .. } = method;
|
||||||
// println!("found method!");
|
|
||||||
m_ident = Some(sig.ident.clone());
|
// if sig.asyncness.is_some() {
|
||||||
}
|
// let name = format!("{}", sig.ident);
|
||||||
}
|
|
||||||
}
|
// if let Some(_method) = service.methods.iter().find(|method| method.name == name) {
|
||||||
}
|
// // println!("found method!");
|
||||||
|
// m_ident = Some(sig.ident.clone());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// let service_name = service.proto_name.clone();
|
// let service_name = service.proto_name.clone();
|
||||||
|
|
||||||
let ts = quote! {
|
// let ts = quote! {
|
||||||
use tonic::_codegen;
|
// use tonic::_codegen;
|
||||||
use proto::*;
|
// use proto::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
// #[derive(Clone)]
|
||||||
pub struct GrpcServer {
|
// pub struct GrpcServer {
|
||||||
inner: std::sync::Arc<#s>,
|
// inner: std::sync::Arc<#s>,
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl GrpcServer {
|
// impl GrpcServer {
|
||||||
fn new(t: #s) -> Self {
|
// fn new(t: #s) -> Self {
|
||||||
Self { inner: std::sync::Arc::new(t) }
|
// Self { inner: std::sync::Arc::new(t) }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl _codegen::Service<()> for GrpcServer {
|
||||||
|
// type Response = Self;
|
||||||
|
// type Error = tonic::error::Never;
|
||||||
|
// type Future = _codegen::Ready<Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
|
// fn poll_ready(&mut self, _cx: &mut _codegen::Context<'_>) -> _codegen::Poll<Result<(), Self::Error>> {
|
||||||
|
// std::task::Poll::Ready(Ok(()))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn call(&mut self, _: ()) -> Self::Future {
|
||||||
|
// _codegen::ok(self.clone())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl _codegen::Service<_codegen::http::Request<tower_h2::RecvBody>> for GrpcServer {
|
||||||
|
// type Response = _codegen::http::Response<tonic::body::BoxAsyncBody>;
|
||||||
|
// type Error = tonic::error::Never;
|
||||||
|
// type Future = _codegen::ResponseFuture2<Self::Response, Self::Error>;
|
||||||
|
|
||||||
|
// fn poll_ready(&mut self, _cx: &mut _codegen::Context<'_>) -> _codegen::Poll<Result<(), Self::Error>> {
|
||||||
|
// Ok(()).into()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn call(&mut self, request: _codegen::http::Request<tower_h2::RecvBody>) -> Self::Future {
|
||||||
|
// let inner = self.inner.clone();
|
||||||
|
|
||||||
|
// match request.uri().path() {
|
||||||
|
// "/helloworld.Greeter/SayHello" => {
|
||||||
|
// use tonic::_codegen::*;
|
||||||
|
// use tonic::*;
|
||||||
|
|
||||||
|
// pub struct SayHello(pub std::sync::Arc<#s>);
|
||||||
|
|
||||||
|
// impl tonic::server::UnaryService<HelloRequest> for SayHello {
|
||||||
|
// type Response = HelloReply;
|
||||||
|
// type Future = Pin<Box<dyn Future<Output = Result<Response<Self::Response>, Status>> + Send + 'static>>;
|
||||||
|
|
||||||
|
// fn call(&mut self, request: Request<HelloRequest>) -> Self::Future {
|
||||||
|
// let inner = self.0.clone();
|
||||||
|
// let fut = async move {
|
||||||
|
// inner.#m_ident(request).await
|
||||||
|
// };
|
||||||
|
// Box::pin(fut)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let inner = self.inner.clone();
|
||||||
|
|
||||||
|
// let fut = async move {
|
||||||
|
// let method = SayHello(inner);
|
||||||
|
// let codec = tonic::codec::ProstCodec::new();
|
||||||
|
// let mut grpc = tonic::server::Grpc::new(codec);
|
||||||
|
// let res = grpc.unary(method, request).await;
|
||||||
|
// Ok(res)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Box::pin(fut)
|
||||||
|
// },
|
||||||
|
// _ => unimplemented!("use grpc unimplemented")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_service(attr: AttributeArgs) -> Service {
|
||||||
|
use syn::{Lit, Meta, MetaNameValue, NestedMeta};
|
||||||
|
|
||||||
|
let service = attr
|
||||||
|
.iter()
|
||||||
|
.filter_map(|i| match i {
|
||||||
|
NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. }))
|
||||||
|
if path.segments.first().unwrap().ident == "service" =>
|
||||||
|
{
|
||||||
|
Some(lit.clone())
|
||||||
}
|
}
|
||||||
}
|
_ => None,
|
||||||
|
})
|
||||||
impl _codegen::Service<()> for GrpcServer {
|
.next();
|
||||||
type Response = Self;
|
|
||||||
type Error = tonic::error::Never;
|
|
||||||
type Future = _codegen::Ready<Result<Self::Response, Self::Error>>;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut _codegen::Context<'_>) -> _codegen::Poll<Result<(), Self::Error>> {
|
|
||||||
std::task::Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, _: ()) -> Self::Future {
|
|
||||||
_codegen::ok(self.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl _codegen::Service<_codegen::http::Request<tower_h2::RecvBody>> for GrpcServer {
|
|
||||||
type Response = _codegen::http::Response<tonic::body::BoxAsyncBody>;
|
|
||||||
type Error = tonic::error::Never;
|
|
||||||
type Future = _codegen::ResponseFuture2<Self::Response, Self::Error>;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut _codegen::Context<'_>) -> _codegen::Poll<Result<(), Self::Error>> {
|
|
||||||
Ok(()).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, request: _codegen::http::Request<tower_h2::RecvBody>) -> Self::Future {
|
|
||||||
let inner = self.inner.clone();
|
|
||||||
|
|
||||||
match request.uri().path() {
|
|
||||||
"/helloworld.Greeter/SayHello" => {
|
|
||||||
use tonic::_codegen::*;
|
|
||||||
use tonic::*;
|
|
||||||
|
|
||||||
pub struct SayHello(pub std::sync::Arc<#s>);
|
|
||||||
|
|
||||||
impl tonic::server::UnaryService<HelloRequest> for SayHello {
|
|
||||||
type Response = HelloReply;
|
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Response<Self::Response>, Status>> + Send + 'static>>;
|
|
||||||
|
|
||||||
fn call(&mut self, request: Request<HelloRequest>) -> Self::Future {
|
|
||||||
let inner = self.0.clone();
|
|
||||||
let fut = async move {
|
|
||||||
inner.#m_ident(request).await
|
|
||||||
};
|
|
||||||
Box::pin(fut)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inner = self.inner.clone();
|
|
||||||
|
|
||||||
|
|
||||||
let fut = async move {
|
|
||||||
let method = SayHello(inner);
|
|
||||||
let codec = tonic::codec::ProstCodec::new();
|
|
||||||
let mut grpc = tonic::server::Grpc::new(codec);
|
|
||||||
let res = grpc.unary(method, request).await;
|
|
||||||
Ok(res)
|
|
||||||
};
|
|
||||||
|
|
||||||
Box::pin(fut)
|
|
||||||
},
|
|
||||||
_ => unimplemented!("use grpc unimplemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
let service_name = match service {
|
||||||
|
Some(Lit::Str(s)) => s.value(),
|
||||||
|
Some(_) => panic!("expected a literal string"),
|
||||||
|
None => panic!("expected a `service = \"package.Service\" attribute"),
|
||||||
};
|
};
|
||||||
|
|
||||||
original.extend(TokenStream::from(ts));
|
let file = format!(
|
||||||
original
|
"{}/{}.json",
|
||||||
|
std::env::var("OUT_DIR").unwrap(),
|
||||||
|
service_name
|
||||||
|
);
|
||||||
|
let json = std::fs::read_to_string(file).unwrap();
|
||||||
|
|
||||||
|
serde_json::from_str(&json).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_service(_attr: TokenStream) -> Service {
|
/// A service descriptor.
|
||||||
Service {
|
#[derive(Debug, Deserialize)]
|
||||||
name: "Greeter".into(),
|
pub(crate) struct Service {
|
||||||
proto_name: "greeter".into(),
|
/// The service name in Rust style.
|
||||||
package: "helloworld".into(),
|
pub name: String,
|
||||||
comments: Comments {
|
/// The service name as it appears in the .proto file.
|
||||||
leading_detached: Vec::new(),
|
pub proto_name: String,
|
||||||
leading: Vec::new(),
|
/// The package name as it appears in the .proto file.
|
||||||
trailing: Vec::new(),
|
pub package: String,
|
||||||
},
|
/// The service methods.
|
||||||
methods: vec![Method {
|
pub methods: Vec<Method>,
|
||||||
name: "say_hello".into(),
|
}
|
||||||
proto_name: "SayHello".into(),
|
|
||||||
comments: Comments {
|
/// A service method descriptor.
|
||||||
leading_detached: Vec::new(),
|
#[derive(Debug, Deserialize)]
|
||||||
leading: Vec::new(),
|
pub(crate) struct Method {
|
||||||
trailing: Vec::new(),
|
/// The name of the method in Rust style.
|
||||||
},
|
pub name: String,
|
||||||
input_type: "HelloRequest".into(),
|
/// The name of the method as it appears in the .proto file.
|
||||||
output_type: "HelloResponse".into(),
|
pub proto_name: String,
|
||||||
input_proto_type: "HelloRequest".into(),
|
/// The input Rust type.
|
||||||
output_proto_type: "HelloResponse".into(),
|
pub input_type: String,
|
||||||
options: Default::default(),
|
/// The output Rust type.
|
||||||
client_streaming: false,
|
pub output_type: String,
|
||||||
server_streaming: false,
|
/// The input Protobuf type.
|
||||||
}],
|
pub input_proto_type: String,
|
||||||
options: Default::default(),
|
/// The output Protobuf type.
|
||||||
}
|
pub output_proto_type: String,
|
||||||
|
// /// The method options.
|
||||||
|
// pub options: prost_types::MethodOptions,
|
||||||
|
/// Identifies if client streams multiple client messages.
|
||||||
|
pub client_streaming: bool,
|
||||||
|
/// Identifies if server streams multiple server messages.
|
||||||
|
pub server_streaming: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
use crate::{Method, Service};
|
||||||
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, LitStr, Path, Type};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ServiceDef {
|
||||||
|
name: Path,
|
||||||
|
name_str: String,
|
||||||
|
package: String,
|
||||||
|
proto_name: String,
|
||||||
|
methods: Vec<(Method, Ident)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_service_impl(item: ItemImpl, mut service: Service) -> 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,
|
||||||
|
methods,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate(service: ServiceDef) -> TokenStream {
|
||||||
|
let service_server = Ident::new(&format!("{}Server", service.name_str), Span::call_site());
|
||||||
|
|
||||||
|
let service_impl = service.name.clone();
|
||||||
|
let methods = generate_methods(&service);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
use tonic::_codegen::*;
|
||||||
|
|
||||||
|
// TODO: impl debug
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct #service_server {
|
||||||
|
inner: std::sync::Arc<#service_impl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #service_server {
|
||||||
|
pub fn new(t: #service_impl) -> Self {
|
||||||
|
let inner = std::sync::Arc::new(t);
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service<()> for #service_server {
|
||||||
|
type Response = Self;
|
||||||
|
type Error = tonic::error::Never;
|
||||||
|
type Future = Ready<Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, _: ()) -> Self::Future {
|
||||||
|
ok(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service<http::Request<tower_h2::RecvBody>> for #service_server {
|
||||||
|
type Response = http::Response<tonic::BoxAsyncBody>;
|
||||||
|
type Error = tonic::error::Never;
|
||||||
|
type Future = BoxFuture<Self::Response, Self::Error>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: http::Request<tower_h2::RecvBody>) -> Self::Future {
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
|
||||||
|
match req.uri().path() {
|
||||||
|
#methods
|
||||||
|
|
||||||
|
_ => unimplemented!("use grpc unimplemented"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_methods(service: &ServiceDef) -> TokenStream {
|
||||||
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
|
for (method, ident) 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 method_stream = generate_unary(method, ident.clone(), service.name.clone());
|
||||||
|
|
||||||
|
let method = quote! {
|
||||||
|
#method_path => {
|
||||||
|
#method_stream
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stream.extend(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_unary(method: &Method, method_ident: Ident, service_impl: Path) -> TokenStream {
|
||||||
|
let service_ident = Ident::new(&method.proto_name, Span::call_site());
|
||||||
|
|
||||||
|
let request: Path = syn::parse_str(&format!("hello_world::{}", method.input_type)).unwrap();
|
||||||
|
let response: Path = syn::parse_str(&format!("hello_world::{}", method.output_type)).unwrap();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
struct #service_ident(pub std::sync::Arc<#service_impl>);
|
||||||
|
|
||||||
|
impl tonic::server::UnaryService<#request> for #service_ident {
|
||||||
|
type Response =#response;
|
||||||
|
type Future = BoxFuture<tonic::Response<Self::Response>, 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
|
||||||
|
};
|
||||||
|
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.unary(method, req).await;
|
||||||
|
Ok(res)
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -14,6 +14,7 @@ mod request;
|
|||||||
mod response;
|
mod response;
|
||||||
mod status;
|
mod status;
|
||||||
|
|
||||||
|
pub use body::{BoxAsyncBody, BoxBody};
|
||||||
pub use request::Request;
|
pub use request::Request;
|
||||||
pub use response::Response;
|
pub use response::Response;
|
||||||
pub use status::{Code, Status};
|
pub use status::{Code, Status};
|
||||||
@@ -39,9 +40,8 @@ pub mod _codegen {
|
|||||||
pub use std::pin::Pin;
|
pub use std::pin::Pin;
|
||||||
pub use std::task::{Context, Poll};
|
pub use std::task::{Context, Poll};
|
||||||
pub use tower_service::Service;
|
pub use tower_service::Service;
|
||||||
pub type ResponseFuture<T> =
|
|
||||||
self::Pin<Box<dyn self::Future<Output = Result<T, crate::Status>> + Send + 'static>>;
|
pub type BoxFuture<T, E> =
|
||||||
pub type ResponseFuture2<T, E> =
|
|
||||||
self::Pin<Box<dyn self::Future<Output = Result<T, E>> + Send + 'static>>;
|
self::Pin<Box<dyn self::Future<Output = Result<T, E>> + Send + 'static>>;
|
||||||
|
|
||||||
pub mod http {
|
pub mod http {
|
||||||
|
|||||||
Reference in New Issue
Block a user