Move codegen to build and remove macros
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"tonic",
|
||||
"tonic-macros",
|
||||
"tonic-build",
|
||||
"tonic-examples",
|
||||
# "tonic-interop",
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/// The request message containing the user's name.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
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,
|
||||
}
|
||||
use tonic::_codegen::*;
|
||||
#[async_trait]
|
||||
pub trait Greeter: Send + Sync + 'static {
|
||||
async fn say_hello(
|
||||
&self,
|
||||
request: tonic::Request<self::HelloRequest>,
|
||||
) -> Result<tonic::Response<self::HelloReply>, tonic::Status>;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct GreeterServer<T: Greeter> {
|
||||
inner: std::sync::Arc<T>,
|
||||
}
|
||||
pub struct GreeterServerSvc<T: Greeter> {
|
||||
inner: std::sync::Arc<T>,
|
||||
}
|
||||
impl<T: Greeter> GreeterServer<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = std::sync::Arc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
impl<T: Greeter> GreeterServerSvc<T> {
|
||||
pub fn new(inner: std::sync::Arc<T>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
impl<T: Greeter, R> Service<R> for GreeterServer<T> {
|
||||
type Response = GreeterServerSvc<T>;
|
||||
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, _: R) -> Self::Future {
|
||||
ok(GreeterServerSvc::new(self.inner.clone()))
|
||||
}
|
||||
}
|
||||
impl<T: Greeter> Service<http::Request<tonic::_codegen::HyperBody>> for GreeterServerSvc<T> {
|
||||
type Response = http::Response<tonic::BoxBody>;
|
||||
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<tonic::_codegen::HyperBody>) -> Self::Future {
|
||||
let inner = self.inner.clone();
|
||||
match req.uri().path() {
|
||||
"/helloworld.Greeter/SayHello" => {
|
||||
struct SayHello<T: Greeter>(pub std::sync::Arc<T>);
|
||||
impl<T: Greeter> tonic::server::UnaryService<self::HelloRequest> for SayHello<T> {
|
||||
type Response = self::HelloReply;
|
||||
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<self::HelloRequest>,
|
||||
) -> Self::Future {
|
||||
let inner = self.0.clone();
|
||||
let fut = async move { inner.say_hello(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, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
_ => unimplemented!("use grpc unimplemented"),
|
||||
}
|
||||
}
|
||||
}
|
||||
use tonic::_codegen::*;
|
||||
pub struct GreeterClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl<T> GreeterClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::BoxBody>,
|
||||
T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static,
|
||||
T::Error: Into<tonic::error::Error>,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Error: Into<tonic::error::Error> + Send,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Data: Into<bytes::Bytes> + Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub async fn ready(&mut self) -> Result<(), tonic::Status> {
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(
|
||||
tonic::Code::Unknown,
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})
|
||||
}
|
||||
pub async fn say_hello(
|
||||
&mut self,
|
||||
request: tonic::Request<self::HelloRequest>,
|
||||
) -> Result<tonic::Response<self::HelloReply>, tonic::Status> {
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static("/helloworld.Greeter/SayHello");
|
||||
self.inner.unary(request, path, codec).await
|
||||
}
|
||||
}
|
||||
impl<T: Clone> Clone for GreeterClient<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/// Points are represented as latitude-longitude pairs in the E7 representation
|
||||
/// (degrees multiplied by 10**7 and rounded to the nearest integer).
|
||||
/// Latitudes should be in the range +/- 90 degrees and longitude should be in
|
||||
/// the range +/- 180 degrees (inclusive).
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Point {
|
||||
#[prost(int32, tag = "1")]
|
||||
pub latitude: i32,
|
||||
#[prost(int32, tag = "2")]
|
||||
pub longitude: i32,
|
||||
}
|
||||
/// A latitude-longitude rectangle, represented as two diagonally opposite
|
||||
/// points "lo" and "hi".
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Rectangle {
|
||||
/// One corner of the rectangle.
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub lo: ::std::option::Option<Point>,
|
||||
/// The other corner of the rectangle.
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub hi: ::std::option::Option<Point>,
|
||||
}
|
||||
/// A feature names something at a given point.
|
||||
///
|
||||
/// If a feature could not be named, the name is empty.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Feature {
|
||||
/// The name of the feature.
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: std::string::String,
|
||||
/// The point where the feature is detected.
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub location: ::std::option::Option<Point>,
|
||||
}
|
||||
/// A RouteNote is a message sent while at a given point.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RouteNote {
|
||||
/// The location from which the message is sent.
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub location: ::std::option::Option<Point>,
|
||||
/// The message to be sent.
|
||||
#[prost(string, tag = "2")]
|
||||
pub message: std::string::String,
|
||||
}
|
||||
/// A RouteSummary is received in response to a RecordRoute rpc.
|
||||
///
|
||||
/// It contains the number of individual points received, the number of
|
||||
/// detected features, and the total distance covered as the cumulative sum of
|
||||
/// the distance between each point.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RouteSummary {
|
||||
/// The number of points received.
|
||||
#[prost(int32, tag = "1")]
|
||||
pub point_count: i32,
|
||||
/// The number of known features passed while traversing the route.
|
||||
#[prost(int32, tag = "2")]
|
||||
pub feature_count: i32,
|
||||
/// The distance covered in metres.
|
||||
#[prost(int32, tag = "3")]
|
||||
pub distance: i32,
|
||||
/// The duration of the traversal in seconds.
|
||||
#[prost(int32, tag = "4")]
|
||||
pub elapsed_time: i32,
|
||||
}
|
||||
use tonic::_codegen::*;
|
||||
#[async_trait]
|
||||
pub trait RouteGuide: Send + Sync + 'static {
|
||||
async fn get_feature(
|
||||
&self,
|
||||
request: tonic::Request<self::Point>,
|
||||
) -> Result<tonic::Response<self::Feature>, tonic::Status>;
|
||||
type ListFeaturesStream: Stream<Item = Result<self::Feature, tonic::Status>>
|
||||
+ Unpin
|
||||
+ Send
|
||||
+ 'static;
|
||||
async fn list_features(
|
||||
&self,
|
||||
request: tonic::Request<self::Rectangle>,
|
||||
) -> Result<tonic::Response<Self::ListFeaturesStream>, tonic::Status>;
|
||||
async fn record_route(
|
||||
&self,
|
||||
request: tonic::Request<tonic::Streaming<self::Point>>,
|
||||
) -> Result<tonic::Response<self::RouteSummary>, tonic::Status>;
|
||||
type RouteChatStream: Stream<Item = Result<self::RouteNote, tonic::Status>>
|
||||
+ Unpin
|
||||
+ Send
|
||||
+ 'static;
|
||||
async fn route_chat(
|
||||
&self,
|
||||
request: tonic::Request<tonic::Streaming<self::RouteNote>>,
|
||||
) -> Result<tonic::Response<Self::RouteChatStream>, tonic::Status>;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct RouteGuideServer<T: RouteGuide> {
|
||||
inner: std::sync::Arc<T>,
|
||||
}
|
||||
pub struct RouteGuideServerSvc<T: RouteGuide> {
|
||||
inner: std::sync::Arc<T>,
|
||||
}
|
||||
impl<T: RouteGuide> RouteGuideServer<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = std::sync::Arc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
impl<T: RouteGuide> RouteGuideServerSvc<T> {
|
||||
pub fn new(inner: std::sync::Arc<T>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
impl<T: RouteGuide, R> Service<R> for RouteGuideServer<T> {
|
||||
type Response = RouteGuideServerSvc<T>;
|
||||
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, _: R) -> Self::Future {
|
||||
ok(RouteGuideServerSvc::new(self.inner.clone()))
|
||||
}
|
||||
}
|
||||
impl<T: RouteGuide> Service<http::Request<tonic::_codegen::HyperBody>> for RouteGuideServerSvc<T> {
|
||||
type Response = http::Response<tonic::BoxBody>;
|
||||
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<tonic::_codegen::HyperBody>) -> Self::Future {
|
||||
let inner = self.inner.clone();
|
||||
match req.uri().path() {
|
||||
"/routeguide.RouteGuide/GetFeature" => {
|
||||
struct GetFeature<T: RouteGuide>(pub std::sync::Arc<T>);
|
||||
impl<T: RouteGuide> tonic::server::UnaryService<self::Point> for GetFeature<T> {
|
||||
type Response = self::Feature;
|
||||
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
|
||||
fn call(&mut self, request: tonic::Request<self::Point>) -> Self::Future {
|
||||
let inner = self.0.clone();
|
||||
let fut = async move { inner.get_feature(request).await };
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = GetFeature(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)
|
||||
}
|
||||
"/routeguide.RouteGuide/ListFeatures" => {
|
||||
struct ListFeatures<T: RouteGuide>(pub std::sync::Arc<T>);
|
||||
impl<T: RouteGuide> tonic::server::ServerStreamingService<self::Rectangle> for ListFeatures<T> {
|
||||
type Response = self::Feature;
|
||||
type ResponseStream = T::ListFeaturesStream;
|
||||
type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
|
||||
fn call(&mut self, request: tonic::Request<self::Rectangle>) -> Self::Future {
|
||||
let inner = self.0.clone();
|
||||
let fut = async move { inner.list_features(request).await };
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = ListFeatures(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)
|
||||
}
|
||||
"/routeguide.RouteGuide/RecordRoute" => {
|
||||
struct RecordRoute<T: RouteGuide>(pub std::sync::Arc<T>);
|
||||
impl<T: RouteGuide> tonic::server::ClientStreamingService<self::Point> for RecordRoute<T> {
|
||||
type Response = self::RouteSummary;
|
||||
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<tonic::Streaming<self::Point>>,
|
||||
) -> Self::Future {
|
||||
let inner = self.0.clone();
|
||||
let fut = async move { inner.record_route(request).await };
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = RecordRoute(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)
|
||||
}
|
||||
"/routeguide.RouteGuide/RouteChat" => {
|
||||
struct RouteChat<T: RouteGuide>(pub std::sync::Arc<T>);
|
||||
impl<T: RouteGuide> tonic::server::StreamingService<self::RouteNote> for RouteChat<T> {
|
||||
type Response = self::RouteNote;
|
||||
type ResponseStream = T::RouteChatStream;
|
||||
type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<tonic::Streaming<self::RouteNote>>,
|
||||
) -> Self::Future {
|
||||
let inner = self.0.clone();
|
||||
let fut = async move { inner.route_chat(request).await };
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = RouteChat(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)
|
||||
}
|
||||
_ => unimplemented!("use grpc unimplemented"),
|
||||
}
|
||||
}
|
||||
}
|
||||
use tonic::_codegen::*;
|
||||
pub struct RouteGuideClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl<T> RouteGuideClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::BoxBody>,
|
||||
T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static,
|
||||
T::Error: Into<tonic::error::Error>,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Error: Into<tonic::error::Error> + Send,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Data: Into<bytes::Bytes> + Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub async fn ready(&mut self) -> Result<(), tonic::Status> {
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(
|
||||
tonic::Code::Unknown,
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})
|
||||
}
|
||||
pub async fn get_feature(
|
||||
&mut self,
|
||||
request: tonic::Request<self::Point>,
|
||||
) -> Result<tonic::Response<self::Feature>, tonic::Status> {
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static("/routeguide.RouteGuide/GetFeature");
|
||||
self.inner.unary(request, path, codec).await
|
||||
}
|
||||
pub async fn list_features(
|
||||
&mut self,
|
||||
request: tonic::Request<self::Rectangle>,
|
||||
) -> Result<tonic::Response<tonic::codec::Streaming<self::Feature>>, tonic::Status> {
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static("/routeguide.RouteGuide/ListFeatures");
|
||||
self.inner.server_streaming(request, path, codec).await
|
||||
}
|
||||
pub async fn record_route<S>(
|
||||
&mut self,
|
||||
request: tonic::Request<S>,
|
||||
) -> Result<tonic::Response<self::RouteSummary>, tonic::Status>
|
||||
where
|
||||
S: tonic::_codegen::Stream<Item = Result<self::Point, tonic::Status>> + Send + 'static,
|
||||
{
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static("/routeguide.RouteGuide/RecordRoute");
|
||||
let request = request.map(|s| Box::pin(s));
|
||||
self.inner.client_streaming(request, path, codec).await
|
||||
}
|
||||
pub async fn route_chat<S>(
|
||||
&mut self,
|
||||
request: tonic::Request<S>,
|
||||
) -> Result<tonic::Response<tonic::codec::Streaming<self::RouteNote>>, tonic::Status>
|
||||
where
|
||||
S: tonic::_codegen::Stream<Item = Result<self::RouteNote, tonic::Status>> + Send + 'static,
|
||||
{
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static("/routeguide.RouteGuide/RouteChat");
|
||||
let request = request.map(|s| Box::pin(s));
|
||||
self.inner.streaming(request, path, codec).await
|
||||
}
|
||||
}
|
||||
impl<T: Clone> Clone for RouteGuideClient<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
prost-build = "0.5"
|
||||
codegen = "0.1"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
syn = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
rustfmt = "0.10"
|
||||
|
||||
@@ -1,9 +1,50 @@
|
||||
use super::{Method, Service};
|
||||
use proc_macro2::TokenStream;
|
||||
use prost_build::{Method, Service};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::Path;
|
||||
|
||||
pub(crate) fn generate(service: Service, proto: String) -> TokenStream {
|
||||
pub(crate) fn generate(service: &Service, proto: &str) -> TokenStream {
|
||||
let service_ident = quote::format_ident!("{}Client", service.name);
|
||||
let methods = generate_methods(service, proto);
|
||||
|
||||
quote! {
|
||||
use tonic::_codegen::*;
|
||||
|
||||
pub struct #service_ident <T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
|
||||
impl<T> #service_ident <T>
|
||||
where T: tonic::client::GrpcService<tonic::BoxBody>,
|
||||
T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static,
|
||||
T::Error: Into<tonic::error::Error>,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Error: Into<tonic::error::Error> + Send,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Data: Into<bytes::Bytes> + Send, {
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub async fn ready(&mut self) -> Result<(), tonic::Status> {
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
|
||||
})
|
||||
}
|
||||
|
||||
#methods
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for #service_ident <T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_methods(service: &Service, proto: &str) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
for method in &service.methods {
|
||||
+33
-78
@@ -1,95 +1,50 @@
|
||||
use prost_build::Config;
|
||||
use serde::Serialize;
|
||||
use std::{io, path};
|
||||
use std::{io, path, process::Command};
|
||||
|
||||
pub fn compile_protos<P>(protos: &[P], includes: &[P]) -> io::Result<()>
|
||||
mod client;
|
||||
mod service;
|
||||
|
||||
pub fn compile_protos<P>(protos: &[P], includes: &[P], package: &str) -> io::Result<()>
|
||||
where
|
||||
P: AsRef<path::Path>,
|
||||
{
|
||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||
let mut config = Config::new();
|
||||
|
||||
config.service_generator(Box::new(ServiceGenerator {}));
|
||||
config.out_dir(&out_dir);
|
||||
config.compile_protos(protos, includes)?;
|
||||
|
||||
config.compile_protos(protos, includes)
|
||||
fmt(&out_dir, &format!("{}.rs", package));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt(out_dir: &str, file: &str) {
|
||||
let out = Command::new("rustfmt")
|
||||
.arg("--emit")
|
||||
.arg("files")
|
||||
.arg("--edition")
|
||||
.arg("2018")
|
||||
.arg(format!("{}/{}", out_dir, file))
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
println!("out: {:?}", out);
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
fn generate(&mut self, service: prost_build::Service, buf: &mut String) {
|
||||
let path = "self";
|
||||
let server = service::generate(&service, path);
|
||||
let code = format!("{}", server);
|
||||
buf.push_str(&code);
|
||||
|
||||
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();
|
||||
let client = client::generate(&service, path);
|
||||
let code = format!("{}", client);
|
||||
buf.push_str(&code);
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use prost_build::{Method, Service};
|
||||
use quote::quote;
|
||||
use syn::{Ident, Lit, LitStr, Path};
|
||||
|
||||
pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream {
|
||||
let methods = generate_methods(&service, proto_path);
|
||||
|
||||
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);
|
||||
let generated_trait = generate_trait(service, proto_path, server_trait.clone());
|
||||
|
||||
quote! {
|
||||
use tonic::_codegen::*;
|
||||
|
||||
#generated_trait
|
||||
|
||||
// TODO: impl debug
|
||||
#[derive(Clone)]
|
||||
pub struct #server_make_service <T: #server_trait > {
|
||||
inner: std::sync::Arc<T>,
|
||||
}
|
||||
|
||||
// TODO: impl debug
|
||||
pub struct #server_service <T: #server_trait > {
|
||||
inner: std::sync::Arc<T>,
|
||||
}
|
||||
|
||||
impl<T: #server_trait > #server_make_service <T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = std::sync::Arc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: #server_trait > #server_service <T> {
|
||||
pub fn new(inner: std::sync::Arc<T>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
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>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _: R) -> Self::Future {
|
||||
ok(#server_service ::new(self.inner.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
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>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: http::Request<tonic::_codegen::HyperBody>) -> Self::Future {
|
||||
let inner = self.inner.clone();
|
||||
|
||||
match req.uri().path() {
|
||||
#methods
|
||||
|
||||
_ => unimplemented!("use grpc unimplemented"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_trait(service: &Service, proto_path: &str, server_trait: Ident) -> TokenStream {
|
||||
let methods = generate_trait_methods(service, proto_path);
|
||||
|
||||
quote! {
|
||||
#[async_trait]
|
||||
pub trait #server_trait : Send + Sync + 'static {
|
||||
#methods
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
for method in &service.methods {
|
||||
let name = quote::format_ident!("{}", method.name);
|
||||
let req_message: Path =
|
||||
syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
|
||||
let res_message: Path =
|
||||
syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
|
||||
|
||||
let method = match (method.client_streaming, method.server_streaming) {
|
||||
(false, false) => {
|
||||
quote! {
|
||||
async fn #name (&self, request: tonic::Request<#req_message>)
|
||||
-> Result<tonic::Response<#res_message>, tonic::Status>;
|
||||
}
|
||||
}
|
||||
(true, false) => {
|
||||
quote! {
|
||||
async fn #name (&self, request: tonic::Request<tonic::Streaming<#req_message>>)
|
||||
-> Result<tonic::Response<#res_message>, tonic::Status>;
|
||||
}
|
||||
}
|
||||
(false, true) => {
|
||||
let stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
|
||||
quote! {
|
||||
type #stream: Stream<Item = Result<#res_message, tonic::Status>> + Unpin + Send + 'static;
|
||||
|
||||
async fn #name (&self, request: tonic::Request<#req_message>)
|
||||
-> Result<tonic::Response<Self::#stream>, tonic::Status>;
|
||||
}
|
||||
}
|
||||
(true, true) => {
|
||||
let stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
|
||||
quote! {
|
||||
type #stream: Stream<Item = Result<#res_message, tonic::Status>> + Unpin + Send + 'static;
|
||||
|
||||
async fn #name (&self, request: tonic::Request<tonic::Streaming<#req_message>>)
|
||||
-> Result<tonic::Response<Self::#stream>, tonic::Status>;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
stream.extend(method);
|
||||
}
|
||||
|
||||
stream
|
||||
}
|
||||
|
||||
fn generate_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
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, proto_path, server_trait),
|
||||
|
||||
(false, true) => {
|
||||
generate_server_streaming(method, ident.clone(), proto_path, server_trait)
|
||||
}
|
||||
(true, false) => {
|
||||
generate_client_streaming(method, ident.clone(), proto_path, server_trait)
|
||||
}
|
||||
|
||||
(true, true) => generate_streaming(method, ident.clone(), proto_path, server_trait),
|
||||
};
|
||||
|
||||
let method = quote! {
|
||||
#method_path => {
|
||||
#method_stream
|
||||
}
|
||||
};
|
||||
stream.extend(method);
|
||||
}
|
||||
|
||||
stream
|
||||
}
|
||||
|
||||
fn generate_unary(
|
||||
method: &Method,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
server_trait: Ident,
|
||||
) -> 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();
|
||||
|
||||
quote! {
|
||||
struct #service_ident <T: #server_trait >(pub std::sync::Arc<T>);
|
||||
|
||||
impl<T: #server_trait > tonic::server::UnaryService<#request> for #service_ident <T> {
|
||||
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)
|
||||
};
|
||||
|
||||
// TODO: implement this future manually
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_server_streaming(
|
||||
method: &Method,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
server_trait: Ident,
|
||||
) -> 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 response_stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
|
||||
// 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 <T: #server_trait >(pub std::sync::Arc<T>);
|
||||
|
||||
impl<T: #server_trait > tonic::server::ServerStreamingService<#request> for #service_ident <T> {
|
||||
type Response = #response;
|
||||
type ResponseStream = T::#response_stream;
|
||||
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
|
||||
|
||||
};
|
||||
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)
|
||||
};
|
||||
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_client_streaming(
|
||||
method: &Method,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
server_trait: Ident,
|
||||
) -> 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();
|
||||
|
||||
quote! {
|
||||
struct #service_ident<T: #server_trait >(pub std::sync::Arc<T>);
|
||||
|
||||
impl<T: #server_trait> tonic::server::ClientStreamingService<#request> for #service_ident <T>
|
||||
{
|
||||
type Response = #response;
|
||||
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
|
||||
|
||||
fn call(&mut self, request: tonic::Request<tonic::Streaming<#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.client_streaming(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_streaming(
|
||||
method: &Method,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
server_trait: Ident,
|
||||
) -> 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 response_stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
|
||||
// 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<T: #server_trait >(pub std::sync::Arc<T>);
|
||||
|
||||
impl<T: #server_trait > tonic::server::StreamingService<#request> for #service_ident <T>
|
||||
{
|
||||
type Response = #response;
|
||||
type ResponseStream = T::#response_stream;
|
||||
type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
|
||||
|
||||
fn call(&mut self, request: tonic::Request<tonic::Streaming<#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.streaming(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ version = "0.1.0"
|
||||
authors = ["Lucio Franco <luciofranco14@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "helloworld-server"
|
||||
path = "src/helloworld/server.rs"
|
||||
@@ -14,9 +12,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"
|
||||
|
||||
@@ -2,12 +2,14 @@ fn main() {
|
||||
tonic_build::compile_protos(
|
||||
&["proto/helloworld/helloworld.proto"],
|
||||
&["proto/helloworld"],
|
||||
"helloworld",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
tonic_build::compile_protos(
|
||||
&["proto/routeguide/route_guide.proto"],
|
||||
&["proto/routeguide"],
|
||||
"routeguide",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use tonic::transport::Channel;
|
||||
|
||||
pub mod hello_world {
|
||||
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
||||
tonic::client!(service = "helloworld.Greeter", proto = "self");
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
use tonic::{Request, Response, Status, Server};
|
||||
use tonic::{Request, Response, Server, Status};
|
||||
|
||||
pub mod hello_world {
|
||||
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
||||
tonic::server!(service = "helloworld.Greeter", proto = "self");
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Default)]
|
||||
pub struct MyGreeter {
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[tonic::server_trait]
|
||||
#[tonic::async_trait]
|
||||
impl hello_world::Greeter for MyGreeter {
|
||||
async fn say_hello(self, request: Request<hello_world::HelloRequest>) -> Result<Response<hello_world::HelloReply>, Status> {
|
||||
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;
|
||||
|
||||
@@ -6,7 +6,6 @@ use tonic::{transport::Channel, Request};
|
||||
|
||||
mod route_guide {
|
||||
include!(concat!(env!("OUT_DIR"), "/routeguide.rs"));
|
||||
tonic::client!(service = "routeguide.RouteGuide", proto = "self");
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::time::Instant;
|
||||
use tokio::sync::{mpsc, Lock};
|
||||
use tonic::transport::Server;
|
||||
use tonic::{Request, Response, Status};
|
||||
use std::pin::Pin;
|
||||
|
||||
pub mod routeguide {
|
||||
include!(concat!(env!("OUT_DIR"), "/routeguide.rs"));
|
||||
@@ -26,9 +27,9 @@ struct State {
|
||||
notes: Lock<HashMap<Point, Vec<RouteNote>>>,
|
||||
}
|
||||
|
||||
#[tonic::server(service = "routeguide.RouteGuide", proto = "routeguide")]
|
||||
impl RouteGuide {
|
||||
pub async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
|
||||
#[tonic::async_trait]
|
||||
impl routeguide::RouteGuide for RouteGuide {
|
||||
async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
|
||||
println!("GetFeature = {:?}", request);
|
||||
|
||||
for feature in &self.state.features[..] {
|
||||
@@ -45,10 +46,12 @@ impl RouteGuide {
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn list_features(
|
||||
type ListFeaturesStream = mpsc::Receiver<Result<Feature, Status>>;
|
||||
|
||||
async fn list_features(
|
||||
&self,
|
||||
request: Request<Rectangle>,
|
||||
) -> Result<Response<mpsc::Receiver<Result<Feature, Status>>>, Status> {
|
||||
) -> Result<Response<Self::ListFeaturesStream>, Status> {
|
||||
use std::thread;
|
||||
|
||||
println!("ListFeatures = {:?}", request);
|
||||
@@ -71,9 +74,9 @@ impl RouteGuide {
|
||||
Ok(Response::new(rx))
|
||||
}
|
||||
|
||||
pub async fn record_route(
|
||||
async fn record_route(
|
||||
&self,
|
||||
request: Request<impl Stream<Item = Result<Point, Status>>>,
|
||||
request: Request<tonic::Streaming<Point>>,
|
||||
) -> Result<Response<RouteSummary>, Status> {
|
||||
println!("RecordRoute");
|
||||
|
||||
@@ -114,10 +117,12 @@ impl RouteGuide {
|
||||
Ok(Response::new(summary))
|
||||
}
|
||||
|
||||
pub async fn route_chat(
|
||||
type RouteChatStream = Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + 'static>>;
|
||||
|
||||
async fn route_chat(
|
||||
&self,
|
||||
request: Request<impl Stream<Item = Result<RouteNote, Status>> + Send + 'static>,
|
||||
) -> Result<Response<impl Stream<Item = Result<RouteNote, Status>> + Send>, Status> {
|
||||
request: Request<tonic::Streaming<RouteNote>>,
|
||||
) -> Result<Response<Self::RouteChatStream>, Status> {
|
||||
println!("RouteChat");
|
||||
|
||||
let stream = request.into_inner();
|
||||
@@ -141,7 +146,8 @@ impl RouteGuide {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Response::new(output))
|
||||
// TODO: Clean this up
|
||||
Ok(Response::new(Box::pin(output) as Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + 'static>>))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +165,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
},
|
||||
};
|
||||
|
||||
let svc = routeguide::RouteGuideServer::new(route_guide);
|
||||
|
||||
Server::builder()
|
||||
.serve(addr, RouteGuideServer::new(route_guide))
|
||||
.serve(addr, svc)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "tonic-macros"
|
||||
version = "0.1.0"
|
||||
authors = ["Lucio Franco <luciofranco14@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@@ -1,155 +0,0 @@
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
mod client;
|
||||
mod service;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use serde::Deserialize;
|
||||
use syn::{AttributeArgs, ItemImpl};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn client(attr: TokenStream) -> TokenStream {
|
||||
let args = syn::parse_macro_input!(attr as AttributeArgs);
|
||||
let (service, proto_path) = load_service(args);
|
||||
|
||||
let service_ident = quote::format_ident!("{}Client", service.name);
|
||||
let methods = client::generate(service, proto_path);
|
||||
|
||||
let output = quote! {
|
||||
use tonic::_codegen::*;
|
||||
|
||||
pub struct #service_ident <T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
|
||||
impl<T> #service_ident <T>
|
||||
where T: tonic::client::GrpcService<tonic::BoxBody>,
|
||||
T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static,
|
||||
T::Error: Into<tonic::error::Error>,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Error: Into<tonic::error::Error> + Send,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Data: Into<bytes::Bytes> + Send, {
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub async fn ready(&mut self) -> Result<(), tonic::Status> {
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
|
||||
})
|
||||
}
|
||||
|
||||
#methods
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for #service_ident <T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(output)
|
||||
}
|
||||
|
||||
#[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 output = service::generate(service, &proto_path);
|
||||
|
||||
TokenStream::from(output)
|
||||
}
|
||||
|
||||
fn load_service(attr: AttributeArgs) -> (Service, String) {
|
||||
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,
|
||||
})
|
||||
.next();
|
||||
|
||||
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"),
|
||||
};
|
||||
|
||||
let service = attr
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. }))
|
||||
if path.segments.first().unwrap().ident == "proto" =>
|
||||
{
|
||||
Some(lit.clone())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.next();
|
||||
|
||||
let proto_path = match service {
|
||||
Some(Lit::Str(s)) => s.value(),
|
||||
Some(_) => panic!("expected a literal string"),
|
||||
None => panic!("expected a `proto = \"my::proto::path\" attribute"),
|
||||
};
|
||||
|
||||
let file = format!(
|
||||
"{}/{}.json",
|
||||
std::env::var("OUT_DIR").unwrap(),
|
||||
service_name
|
||||
);
|
||||
let json = std::fs::read_to_string(file).unwrap();
|
||||
let svc = serde_json::from_str(&json).unwrap();
|
||||
|
||||
(svc, proto_path)
|
||||
}
|
||||
|
||||
/// A service descriptor.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) 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, Deserialize)]
|
||||
pub(crate) 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,
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
use crate::{Method, Service};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{Ident, Lit, LitStr, Path};
|
||||
|
||||
pub(crate) fn generate(service: Service, proto_path: &str) -> TokenStream {
|
||||
let methods = generate_methods(&service, proto_path);
|
||||
|
||||
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 <T: #server_trait > {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
// TODO: impl debug
|
||||
pub struct #server_service <T: #server_trait > {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T: #server_trait > #server_make_service <T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: #server_trait > #server_service <T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
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>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _: R) -> Self::Future {
|
||||
ok(#server_service ::new(self.inner.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
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>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: http::Request<tonic::_codegen::HyperBody>) -> Self::Future {
|
||||
let inner = self.inner.clone();
|
||||
|
||||
match req.uri().path() {
|
||||
#methods
|
||||
|
||||
_ => unimplemented!("use grpc unimplemented"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
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,
|
||||
proto_path,
|
||||
server_trait
|
||||
),
|
||||
|
||||
_ => unimplemented!()
|
||||
|
||||
// (false, true) => generate_server_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! {
|
||||
#method_path => {
|
||||
#method_stream
|
||||
}
|
||||
};
|
||||
stream.extend(method);
|
||||
}
|
||||
|
||||
stream
|
||||
}
|
||||
|
||||
fn generate_unary(
|
||||
method: &Method,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
server_trait: Ident,
|
||||
) -> 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();
|
||||
|
||||
quote! {
|
||||
struct #service_ident <T: #server_trait >(pub T);
|
||||
|
||||
impl<T: #server_trait > tonic::server::UnaryService<#request> for #service_ident <T> {
|
||||
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)
|
||||
};
|
||||
|
||||
// TODO: implement this future manually
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// // 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>;
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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();
|
||||
|
||||
// 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>;
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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();
|
||||
|
||||
// // 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>;
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
@@ -7,7 +7,6 @@ edition = "2018"
|
||||
[dependencies]
|
||||
futures-core-preview = "=0.3.0-alpha.18"
|
||||
futures-util-preview = "=0.3.0-alpha.18"
|
||||
tonic-macros = { path = "../tonic-macros" }
|
||||
tracing = "0.1"
|
||||
http = "0.1.14"
|
||||
base64 = "0.10"
|
||||
|
||||
+3
-4
@@ -45,20 +45,19 @@ mod request;
|
||||
mod response;
|
||||
mod status;
|
||||
|
||||
pub use async_trait::async_trait;
|
||||
#[doc(inline, hidden)]
|
||||
pub use body::BoxBody;
|
||||
#[doc(inline)]
|
||||
pub use codec::Streaming;
|
||||
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;
|
||||
|
||||
@@ -93,7 +93,7 @@ where
|
||||
req: http::Request<B>,
|
||||
) -> http::Response<BoxBody>
|
||||
where
|
||||
S: ClientStreamingService<Streaming<T::Decode>, Response = T::Encode>,
|
||||
S: ClientStreamingService<T::Decode, Response = T::Encode>,
|
||||
B: Body + Send + 'static,
|
||||
B::Data: Into<Bytes> + Send + 'static,
|
||||
B::Error: Into<crate::Error> + Send + 'static,
|
||||
@@ -113,7 +113,7 @@ where
|
||||
req: http::Request<B>,
|
||||
) -> http::Response<BoxBody>
|
||||
where
|
||||
S: StreamingService<Streaming<T::Decode>, Response = T::Encode> + Send,
|
||||
S: StreamingService<T::Decode, Response = T::Encode> + Send,
|
||||
S::ResponseStream: Send + 'static,
|
||||
B: Body + Send + 'static,
|
||||
B::Data: Into<Bytes> + Send,
|
||||
|
||||
+13
-15
@@ -1,4 +1,4 @@
|
||||
use crate::{Request, Response, Status};
|
||||
use crate::{Request, Response, Status, Streaming};
|
||||
use futures_core::Stream;
|
||||
use std::future::Future;
|
||||
use tower_service::Service;
|
||||
@@ -66,7 +66,7 @@ where
|
||||
///
|
||||
/// Existing tower_service::Service implementations with the correct form will
|
||||
/// automatically implement `ClientStreamingService`.
|
||||
pub trait ClientStreamingService<RequestStream> {
|
||||
pub trait ClientStreamingService<R> {
|
||||
/// Protobuf response message type
|
||||
type Response;
|
||||
|
||||
@@ -74,18 +74,17 @@ pub trait ClientStreamingService<RequestStream> {
|
||||
type Future: Future<Output = Result<Response<Self::Response>, Status>>;
|
||||
|
||||
/// Call the service
|
||||
fn call(&mut self, request: Request<RequestStream>) -> Self::Future;
|
||||
fn call(&mut self, request: Request<Streaming<R>>) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<T, M1, M2, S> ClientStreamingService<S> for T
|
||||
impl<T, M1, M2> ClientStreamingService<M1> for T
|
||||
where
|
||||
T: Service<Request<S>, Response = Response<M2>, Error = crate::Status>,
|
||||
S: Stream<Item = Result<M1, crate::Status>>,
|
||||
T: Service<Request<Streaming<M1>>, Response = Response<M2>, Error = crate::Status>,
|
||||
{
|
||||
type Response = M2;
|
||||
type Future = T::Future;
|
||||
|
||||
fn call(&mut self, request: Request<S>) -> Self::Future {
|
||||
fn call(&mut self, request: Request<Streaming<M1>>) -> Self::Future {
|
||||
Service::call(self, request)
|
||||
}
|
||||
}
|
||||
@@ -94,7 +93,7 @@ where
|
||||
///
|
||||
/// Existing tower_service::Service implementations with the correct form will
|
||||
/// automatically implement `StreamingService`.
|
||||
pub trait StreamingService<RequestStream> {
|
||||
pub trait StreamingService<R> {
|
||||
/// Protobuf response message type
|
||||
type Response;
|
||||
|
||||
@@ -105,20 +104,19 @@ pub trait StreamingService<RequestStream> {
|
||||
type Future: Future<Output = Result<Response<Self::ResponseStream>, Status>>;
|
||||
|
||||
/// Call the service
|
||||
fn call(&mut self, request: Request<RequestStream>) -> Self::Future;
|
||||
fn call(&mut self, request: Request<Streaming<R>>) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<T, S1, S2, M1, M2> StreamingService<S1> for T
|
||||
impl<T, S, M1, M2> StreamingService<M1> for T
|
||||
where
|
||||
T: Service<Request<S1>, Response = Response<S2>, Error = crate::Status>,
|
||||
S1: Stream<Item = Result<M1, crate::Status>>,
|
||||
S2: Stream<Item = Result<M2, crate::Status>>,
|
||||
T: Service<Request<Streaming<M1>>, Response = Response<S>, Error = crate::Status>,
|
||||
S: Stream<Item = Result<M2, crate::Status>>,
|
||||
{
|
||||
type Response = M2;
|
||||
type ResponseStream = S2;
|
||||
type ResponseStream = S;
|
||||
type Future = T::Future;
|
||||
|
||||
fn call(&mut self, request: Request<S1>) -> Self::Future {
|
||||
fn call(&mut self, request: Request<Streaming<M1>>) -> Self::Future {
|
||||
Service::call(self, request)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user