Move codegen to build and remove macros
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"tonic",
|
"tonic",
|
||||||
"tonic-macros",
|
|
||||||
"tonic-build",
|
"tonic-build",
|
||||||
"tonic-examples",
|
"tonic-examples",
|
||||||
# "tonic-interop",
|
# "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]
|
[dependencies]
|
||||||
prost-build = "0.5"
|
prost-build = "0.5"
|
||||||
codegen = "0.1"
|
syn = "1.0"
|
||||||
serde_json = "1.0"
|
quote = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
proc-macro2 = "1.0"
|
||||||
|
rustfmt = "0.10"
|
||||||
|
|||||||
@@ -1,9 +1,50 @@
|
|||||||
use super::{Method, Service};
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
use prost_build::{Method, Service};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::Path;
|
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();
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
for method in &service.methods {
|
for method in &service.methods {
|
||||||
+33
-78
@@ -1,95 +1,50 @@
|
|||||||
use prost_build::Config;
|
use prost_build::Config;
|
||||||
use serde::Serialize;
|
use std::{io, path, process::Command};
|
||||||
use std::{io, path};
|
|
||||||
|
|
||||||
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
|
where
|
||||||
P: AsRef<path::Path>,
|
P: AsRef<path::Path>,
|
||||||
{
|
{
|
||||||
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
|
|
||||||
config.service_generator(Box::new(ServiceGenerator {}));
|
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 {}
|
pub struct ServiceGenerator {}
|
||||||
|
|
||||||
impl prost_build::ServiceGenerator for ServiceGenerator {
|
impl prost_build::ServiceGenerator for ServiceGenerator {
|
||||||
fn generate(&mut self, service: prost_build::Service, _buf: &mut String) {
|
fn generate(&mut self, service: prost_build::Service, buf: &mut String) {
|
||||||
let file = format!(
|
let path = "self";
|
||||||
"{}/{}.{}.json",
|
let server = service::generate(&service, path);
|
||||||
std::env::var("OUT_DIR").unwrap(),
|
let code = format!("{}", server);
|
||||||
service.package,
|
buf.push_str(&code);
|
||||||
service.name
|
|
||||||
);
|
|
||||||
|
|
||||||
let svc = Service {
|
let client = client::generate(&service, path);
|
||||||
name: service.name,
|
let code = format!("{}", client);
|
||||||
proto_name: service.proto_name,
|
buf.push_str(&code);
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 <[email protected]>"]
|
authors = ["Lucio Franco <[email protected]>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "helloworld-server"
|
name = "helloworld-server"
|
||||||
path = "src/helloworld/server.rs"
|
path = "src/helloworld/server.rs"
|
||||||
@@ -14,9 +12,9 @@ path = "src/helloworld/server.rs"
|
|||||||
name = "helloworld-client"
|
name = "helloworld-client"
|
||||||
path = "src/helloworld/client.rs"
|
path = "src/helloworld/client.rs"
|
||||||
|
|
||||||
# [[bin]]
|
[[bin]]
|
||||||
# name = "routeguide-server"
|
name = "routeguide-server"
|
||||||
# path = "src/routeguide/server.rs"
|
path = "src/routeguide/server.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "routeguide-client"
|
name = "routeguide-client"
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ fn main() {
|
|||||||
tonic_build::compile_protos(
|
tonic_build::compile_protos(
|
||||||
&["proto/helloworld/helloworld.proto"],
|
&["proto/helloworld/helloworld.proto"],
|
||||||
&["proto/helloworld"],
|
&["proto/helloworld"],
|
||||||
|
"helloworld",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
tonic_build::compile_protos(
|
tonic_build::compile_protos(
|
||||||
&["proto/routeguide/route_guide.proto"],
|
&["proto/routeguide/route_guide.proto"],
|
||||||
&["proto/routeguide"],
|
&["proto/routeguide"],
|
||||||
|
"routeguide",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use tonic::transport::Channel;
|
|||||||
|
|
||||||
pub mod hello_world {
|
pub mod hello_world {
|
||||||
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
||||||
tonic::client!(service = "helloworld.Greeter", proto = "self");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
use tonic::{Request, Response, Status, Server};
|
use tonic::{Request, Response, Server, Status};
|
||||||
|
|
||||||
pub mod hello_world {
|
pub mod hello_world {
|
||||||
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
||||||
tonic::server!(service = "helloworld.Greeter", proto = "self");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default)]
|
||||||
pub struct MyGreeter {
|
pub struct MyGreeter {
|
||||||
data: String,
|
data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tonic::server_trait]
|
#[tonic::async_trait]
|
||||||
impl hello_world::Greeter for MyGreeter {
|
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);
|
println!("Got a request: {:?}", request);
|
||||||
|
|
||||||
let string = &self.data;
|
let string = &self.data;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use tonic::{transport::Channel, Request};
|
|||||||
|
|
||||||
mod route_guide {
|
mod route_guide {
|
||||||
include!(concat!(env!("OUT_DIR"), "/routeguide.rs"));
|
include!(concat!(env!("OUT_DIR"), "/routeguide.rs"));
|
||||||
tonic::client!(service = "routeguide.RouteGuide", proto = "self");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::time::Instant;
|
|||||||
use tokio::sync::{mpsc, Lock};
|
use tokio::sync::{mpsc, Lock};
|
||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
pub mod routeguide {
|
pub mod routeguide {
|
||||||
include!(concat!(env!("OUT_DIR"), "/routeguide.rs"));
|
include!(concat!(env!("OUT_DIR"), "/routeguide.rs"));
|
||||||
@@ -26,9 +27,9 @@ struct State {
|
|||||||
notes: Lock<HashMap<Point, Vec<RouteNote>>>,
|
notes: Lock<HashMap<Point, Vec<RouteNote>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tonic::server(service = "routeguide.RouteGuide", proto = "routeguide")]
|
#[tonic::async_trait]
|
||||||
impl RouteGuide {
|
impl routeguide::RouteGuide for RouteGuide {
|
||||||
pub async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
|
async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
|
||||||
println!("GetFeature = {:?}", request);
|
println!("GetFeature = {:?}", request);
|
||||||
|
|
||||||
for feature in &self.state.features[..] {
|
for feature in &self.state.features[..] {
|
||||||
@@ -45,10 +46,12 @@ impl RouteGuide {
|
|||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_features(
|
type ListFeaturesStream = mpsc::Receiver<Result<Feature, Status>>;
|
||||||
|
|
||||||
|
async fn list_features(
|
||||||
&self,
|
&self,
|
||||||
request: Request<Rectangle>,
|
request: Request<Rectangle>,
|
||||||
) -> Result<Response<mpsc::Receiver<Result<Feature, Status>>>, Status> {
|
) -> Result<Response<Self::ListFeaturesStream>, Status> {
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
println!("ListFeatures = {:?}", request);
|
println!("ListFeatures = {:?}", request);
|
||||||
@@ -71,9 +74,9 @@ impl RouteGuide {
|
|||||||
Ok(Response::new(rx))
|
Ok(Response::new(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn record_route(
|
async fn record_route(
|
||||||
&self,
|
&self,
|
||||||
request: Request<impl Stream<Item = Result<Point, Status>>>,
|
request: Request<tonic::Streaming<Point>>,
|
||||||
) -> Result<Response<RouteSummary>, Status> {
|
) -> Result<Response<RouteSummary>, Status> {
|
||||||
println!("RecordRoute");
|
println!("RecordRoute");
|
||||||
|
|
||||||
@@ -114,10 +117,12 @@ impl RouteGuide {
|
|||||||
Ok(Response::new(summary))
|
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,
|
&self,
|
||||||
request: Request<impl Stream<Item = Result<RouteNote, Status>> + Send + 'static>,
|
request: Request<tonic::Streaming<RouteNote>>,
|
||||||
) -> Result<Response<impl Stream<Item = Result<RouteNote, Status>> + Send>, Status> {
|
) -> Result<Response<Self::RouteChatStream>, Status> {
|
||||||
println!("RouteChat");
|
println!("RouteChat");
|
||||||
|
|
||||||
let stream = request.into_inner();
|
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()
|
Server::builder()
|
||||||
.serve(addr, RouteGuideServer::new(route_guide))
|
.serve(addr, svc)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "tonic-macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Lucio Franco <[email protected]>"]
|
|
||||||
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]
|
[dependencies]
|
||||||
futures-core-preview = "=0.3.0-alpha.18"
|
futures-core-preview = "=0.3.0-alpha.18"
|
||||||
futures-util-preview = "=0.3.0-alpha.18"
|
futures-util-preview = "=0.3.0-alpha.18"
|
||||||
tonic-macros = { path = "../tonic-macros" }
|
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
http = "0.1.14"
|
http = "0.1.14"
|
||||||
base64 = "0.10"
|
base64 = "0.10"
|
||||||
|
|||||||
+3
-4
@@ -45,20 +45,19 @@ mod request;
|
|||||||
mod response;
|
mod response;
|
||||||
mod status;
|
mod status;
|
||||||
|
|
||||||
|
pub use async_trait::async_trait;
|
||||||
#[doc(inline, hidden)]
|
#[doc(inline, hidden)]
|
||||||
pub use body::BoxBody;
|
pub use body::BoxBody;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use codec::Streaming;
|
||||||
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};
|
||||||
pub use tonic_macros::{client, server};
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use transport::{Channel, Server};
|
pub use transport::{Channel, Server};
|
||||||
|
|
||||||
pub(crate) use error::Error;
|
pub(crate) use error::Error;
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use async_trait::async_trait as server_trait;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod _codegen {
|
pub mod _codegen {
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ where
|
|||||||
req: http::Request<B>,
|
req: http::Request<B>,
|
||||||
) -> http::Response<BoxBody>
|
) -> http::Response<BoxBody>
|
||||||
where
|
where
|
||||||
S: ClientStreamingService<Streaming<T::Decode>, Response = T::Encode>,
|
S: ClientStreamingService<T::Decode, Response = T::Encode>,
|
||||||
B: Body + Send + 'static,
|
B: Body + Send + 'static,
|
||||||
B::Data: Into<Bytes> + Send + 'static,
|
B::Data: Into<Bytes> + Send + 'static,
|
||||||
B::Error: Into<crate::Error> + Send + 'static,
|
B::Error: Into<crate::Error> + Send + 'static,
|
||||||
@@ -113,7 +113,7 @@ where
|
|||||||
req: http::Request<B>,
|
req: http::Request<B>,
|
||||||
) -> http::Response<BoxBody>
|
) -> http::Response<BoxBody>
|
||||||
where
|
where
|
||||||
S: StreamingService<Streaming<T::Decode>, Response = T::Encode> + Send,
|
S: StreamingService<T::Decode, Response = T::Encode> + Send,
|
||||||
S::ResponseStream: Send + 'static,
|
S::ResponseStream: Send + 'static,
|
||||||
B: Body + Send + 'static,
|
B: Body + Send + 'static,
|
||||||
B::Data: Into<Bytes> + Send,
|
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 futures_core::Stream;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
@@ -66,7 +66,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Existing tower_service::Service implementations with the correct form will
|
/// Existing tower_service::Service implementations with the correct form will
|
||||||
/// automatically implement `ClientStreamingService`.
|
/// automatically implement `ClientStreamingService`.
|
||||||
pub trait ClientStreamingService<RequestStream> {
|
pub trait ClientStreamingService<R> {
|
||||||
/// Protobuf response message type
|
/// Protobuf response message type
|
||||||
type Response;
|
type Response;
|
||||||
|
|
||||||
@@ -74,18 +74,17 @@ pub trait ClientStreamingService<RequestStream> {
|
|||||||
type Future: Future<Output = Result<Response<Self::Response>, Status>>;
|
type Future: Future<Output = Result<Response<Self::Response>, Status>>;
|
||||||
|
|
||||||
/// Call the service
|
/// 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
|
where
|
||||||
T: Service<Request<S>, Response = Response<M2>, Error = crate::Status>,
|
T: Service<Request<Streaming<M1>>, Response = Response<M2>, Error = crate::Status>,
|
||||||
S: Stream<Item = Result<M1, crate::Status>>,
|
|
||||||
{
|
{
|
||||||
type Response = M2;
|
type Response = M2;
|
||||||
type Future = T::Future;
|
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)
|
Service::call(self, request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,7 +93,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Existing tower_service::Service implementations with the correct form will
|
/// Existing tower_service::Service implementations with the correct form will
|
||||||
/// automatically implement `StreamingService`.
|
/// automatically implement `StreamingService`.
|
||||||
pub trait StreamingService<RequestStream> {
|
pub trait StreamingService<R> {
|
||||||
/// Protobuf response message type
|
/// Protobuf response message type
|
||||||
type Response;
|
type Response;
|
||||||
|
|
||||||
@@ -105,20 +104,19 @@ pub trait StreamingService<RequestStream> {
|
|||||||
type Future: Future<Output = Result<Response<Self::ResponseStream>, Status>>;
|
type Future: Future<Output = Result<Response<Self::ResponseStream>, Status>>;
|
||||||
|
|
||||||
/// Call the service
|
/// 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
|
where
|
||||||
T: Service<Request<S1>, Response = Response<S2>, Error = crate::Status>,
|
T: Service<Request<Streaming<M1>>, Response = Response<S>, Error = crate::Status>,
|
||||||
S1: Stream<Item = Result<M1, crate::Status>>,
|
S: Stream<Item = Result<M2, crate::Status>>,
|
||||||
S2: Stream<Item = Result<M2, crate::Status>>,
|
|
||||||
{
|
{
|
||||||
type Response = M2;
|
type Response = M2;
|
||||||
type ResponseStream = S2;
|
type ResponseStream = S;
|
||||||
type Future = T::Future;
|
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)
|
Service::call(self, request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user