From af753940253c6ee1a7b3e87efa5be3a2c47f856e Mon Sep 17 00:00:00 2001 From: Joao Neves Date: Wed, 13 Nov 2019 17:46:18 +0100 Subject: [PATCH] Enable multiple services in same package across multiple files Prior to this change, for each _file_ containing one or more services, the code generator would generate with the following modules: ``` pub mod client { //... } pub mod server { //... } ``` While this works fine if all the services in the same protobuf package are declared in a single file, this breaks horribly if multiple files belonging to the same protobuf package declare services. In that case, the code generator would generate several modules with the same name in the same `.rs` file, which is invalid. For instance, given two files `foo.proto` and `bar.proto` like: ``` // foo.proto package mypackage; service Foo { } // bar.proto package mypackage; service Bar { } ``` The generated code would result in a `mypackage.rs` such as: ``` // Generated from foo.proto pub mod client { //... } pub mod server { //... } // Generated from bar.proto pub mod client { //... } pub mod server { //... } ``` This change makes it so the name of the service is prepended to the generated module, and therefore we avoid module name collisions. After this change, given the two proto files mentioned previously, we will generate the following modules inside `mypackage.rs`: ``` pub mod foo_client { //... } pub mod foo_server { //... } pub mod bar_client { //... } pub mod bar_server { //... } ``` One codebase where this scenario could be found is in [Open Match](https://github.com/googleforgames/open-match/tree/master/api) --- tests/included_service/src/lib.rs | 2 +- tonic-build/src/client.rs | 63 ++++++++------- tonic-build/src/lib.rs | 16 +--- tonic-build/src/server.rs | 83 +++++++++++--------- tonic-examples/src/authentication/client.rs | 2 +- tonic-examples/src/authentication/server.rs | 4 +- tonic-examples/src/gcp/client.rs | 2 +- tonic-examples/src/helloworld/client.rs | 2 +- tonic-examples/src/helloworld/server.rs | 2 +- tonic-examples/src/load_balance/client.rs | 2 +- tonic-examples/src/load_balance/server.rs | 4 +- tonic-examples/src/multiplex/client.rs | 4 +- tonic-examples/src/multiplex/server.rs | 4 +- tonic-examples/src/routeguide/client.rs | 2 +- tonic-examples/src/routeguide/server.rs | 6 +- tonic-examples/src/tls/client.rs | 2 +- tonic-examples/src/tls/server.rs | 4 +- tonic-examples/src/tls_client_auth/client.rs | 2 +- tonic-examples/src/tls_client_auth/server.rs | 4 +- tonic-interop/src/client.rs | 5 +- tonic-interop/src/server.rs | 7 +- 21 files changed, 114 insertions(+), 108 deletions(-) diff --git a/tests/included_service/src/lib.rs b/tests/included_service/src/lib.rs index d8e6f1026..be308497f 100644 --- a/tests/included_service/src/lib.rs +++ b/tests/included_service/src/lib.rs @@ -5,4 +5,4 @@ pub mod pb { // Ensure that an RPC service, defined before including a file that defines // another service in a different protocol buffer package, is not incorrectly // cleared from the context of its package. -type _Test = dyn pb::server::TopService; +type _Test = dyn pb::topservice_server::TopService; diff --git a/tonic-build/src/client.rs b/tonic-build/src/client.rs index e0a688956..aca122b7c 100644 --- a/tonic-build/src/client.rs +++ b/tonic-build/src/client.rs @@ -5,44 +5,51 @@ use quote::{format_ident, quote}; pub(crate) fn generate(service: &Service, proto: &str) -> TokenStream { let service_ident = quote::format_ident!("{}Client", service.name); + let client_mod = quote::format_ident!("{}_client", service.name.to_ascii_lowercase()); let methods = generate_methods(service, proto); let connect = generate_connect(&service_ident); let service_doc = generate_doc_comments(&service.comments.leading); quote! { - #service_doc - pub struct #service_ident { - inner: tonic::client::Grpc, - } - - #connect - - impl #service_ident - where T: tonic::client::GrpcService, - T::ResponseBody: Body + HttpBody + Send + 'static, - T::Error: Into, - ::Error: Into + Send, - ::Data: Into + Send, { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } + /// Generated server implementations. + pub mod #client_mod { + #![allow(unused_variables, dead_code, missing_docs)] + use tonic::codegen::*; + + #service_doc + pub struct #service_ident { + inner: tonic::client::Grpc, } - /// Check if the service is ready. - 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())) - }) - } + #connect + + impl #service_ident + where T: tonic::client::GrpcService, + T::ResponseBody: Body + HttpBody + Send + 'static, + T::Error: Into, + ::Error: Into + Send, + ::Data: Into + Send, { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } - #methods - } + /// Check if the service is ready. + 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 Clone for #service_ident { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), + impl Clone for #service_ident { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } } } } diff --git a/tonic-build/src/lib.rs b/tonic-build/src/lib.rs index 2494b178a..aca9a1c58 100644 --- a/tonic-build/src/lib.rs +++ b/tonic-build/src/lib.rs @@ -251,13 +251,7 @@ impl prost_build::ServiceGenerator for ServiceGenerator { let clients = &self.clients; let client_service = quote::quote! { - /// Generated client implementations. - pub mod client { - #![allow(unused_variables, dead_code, missing_docs)] - use tonic::codegen::*; - - #clients - } + #clients }; let code = format!("{}", client_service); @@ -270,13 +264,7 @@ impl prost_build::ServiceGenerator for ServiceGenerator { let servers = &self.servers; let server_service = quote::quote! { - /// Generated server implementations. - pub mod server { - #![allow(unused_variables, dead_code, missing_docs)] - use tonic::codegen::*; - - #servers - } + #servers }; let code = format!("{}", server_service); diff --git a/tonic-build/src/server.rs b/tonic-build/src/server.rs index 9442eb262..fab3fe834 100644 --- a/tonic-build/src/server.rs +++ b/tonic-build/src/server.rs @@ -9,6 +9,7 @@ pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream { let server_service = quote::format_ident!("{}Server", service.name); let server_trait = quote::format_ident!("{}", service.name); + let server_mod = quote::format_ident!("{}_server", service.name.to_ascii_lowercase()); let generated_trait = generate_trait(service, proto_path, server_trait.clone()); let service_doc = generate_doc_comments(&service.comments.leading); @@ -17,56 +18,62 @@ pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream { let transport = generate_transport(&server_service, &server_trait, &path); quote! { - #generated_trait - - #service_doc - #[derive(Debug)] - #[doc(hidden)] - pub struct #server_service { - inner: Arc, - } + /// Generated server implementations. + pub mod #server_mod { + #![allow(unused_variables, dead_code, missing_docs)] + use tonic::codegen::*; + + #generated_trait + + #service_doc + #[derive(Debug)] + #[doc(hidden)] + pub struct #server_service { + inner: Arc, + } - impl #server_service { - pub fn new(inner: T) -> Self { - let inner = Arc::new(inner); - Self { inner } + impl #server_service { + pub fn new(inner: T) -> Self { + let inner = Arc::new(inner); + Self { inner } + } } - } - impl Service> for #server_service { - type Response = http::Response; - type Error = Never; - type Future = BoxFuture; + impl Service> for #server_service { + type Response = http::Response; + type Error = Never; + type Future = BoxFuture; - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } - fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); - match req.uri().path() { - #methods + match req.uri().path() { + #methods - _ => Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .body(tonic::body::BoxBody::empty()) - .unwrap()) - }), + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .body(tonic::body::BoxBody::empty()) + .unwrap()) + }), + } } } - } - impl Clone for #server_service { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { inner } + impl Clone for #server_service { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { inner } + } } - } - #transport + #transport + } } } diff --git a/tonic-examples/src/authentication/client.rs b/tonic-examples/src/authentication/client.rs index b51477391..b97b02b37 100644 --- a/tonic-examples/src/authentication/client.rs +++ b/tonic-examples/src/authentication/client.rs @@ -3,7 +3,7 @@ pub mod pb { } use http::header::HeaderValue; -use pb::{client::EchoClient, EchoRequest}; +use pb::{echo_client::EchoClient, EchoRequest}; use tonic::transport::Channel; #[tokio::main] diff --git a/tonic-examples/src/authentication/server.rs b/tonic-examples/src/authentication/server.rs index cf49da0c0..26dc4a667 100644 --- a/tonic-examples/src/authentication/server.rs +++ b/tonic-examples/src/authentication/server.rs @@ -14,7 +14,7 @@ type Stream = VecDeque>; pub struct EchoServer; #[tonic::async_trait] -impl pb::server::Echo for EchoServer { +impl pb::echo_server::Echo for EchoServer { async fn unary_echo(&self, request: Request) -> EchoResult { let message = request.into_inner().message; Ok(Response::new(EchoResponse { message })) @@ -78,7 +78,7 @@ async fn main() -> Result<(), Box> { } } }) - .add_service(pb::server::EchoServer::new(server)) + .add_service(pb::echo_server::EchoServer::new(server)) .serve(addr) .await?; diff --git a/tonic-examples/src/gcp/client.rs b/tonic-examples/src/gcp/client.rs index c698ba8b0..9138de599 100644 --- a/tonic-examples/src/gcp/client.rs +++ b/tonic-examples/src/gcp/client.rs @@ -2,7 +2,7 @@ pub mod api { tonic::include_proto!("google.pubsub.v1"); } -use api::{client::PublisherClient, ListTopicsRequest}; +use api::{publisher_client::PublisherClient, ListTopicsRequest}; use http::header::HeaderValue; use tonic::{ transport::{Certificate, Channel, ClientTlsConfig}, diff --git a/tonic-examples/src/helloworld/client.rs b/tonic-examples/src/helloworld/client.rs index 227be6bc6..486e1d823 100644 --- a/tonic-examples/src/helloworld/client.rs +++ b/tonic-examples/src/helloworld/client.rs @@ -2,7 +2,7 @@ pub mod hello_world { tonic::include_proto!("helloworld"); } -use hello_world::{client::GreeterClient, HelloRequest}; +use hello_world::{greeter_client::GreeterClient, HelloRequest}; #[tokio::main] async fn main() -> Result<(), Box> { diff --git a/tonic-examples/src/helloworld/server.rs b/tonic-examples/src/helloworld/server.rs index 211c4e692..23a3454e4 100644 --- a/tonic-examples/src/helloworld/server.rs +++ b/tonic-examples/src/helloworld/server.rs @@ -5,7 +5,7 @@ pub mod hello_world { } use hello_world::{ - server::{Greeter, GreeterServer}, + greeter_server::{Greeter, GreeterServer}, HelloReply, HelloRequest, }; diff --git a/tonic-examples/src/load_balance/client.rs b/tonic-examples/src/load_balance/client.rs index 904605aef..169546ce1 100644 --- a/tonic-examples/src/load_balance/client.rs +++ b/tonic-examples/src/load_balance/client.rs @@ -2,7 +2,7 @@ pub mod pb { tonic::include_proto!("grpc.examples.echo"); } -use pb::{client::EchoClient, EchoRequest}; +use pb::{echo_client::EchoClient, EchoRequest}; use tonic::transport::Channel; #[tokio::main] diff --git a/tonic-examples/src/load_balance/server.rs b/tonic-examples/src/load_balance/server.rs index ee68be498..7448d5271 100644 --- a/tonic-examples/src/load_balance/server.rs +++ b/tonic-examples/src/load_balance/server.rs @@ -16,7 +16,7 @@ pub struct EchoServer { } #[tonic::async_trait] -impl pb::server::Echo for EchoServer { +impl pb::echo_server::Echo for EchoServer { async fn unary_echo(&self, request: Request) -> EchoResult { let message = format!("{} (from {})", request.into_inner().message, self.addr); @@ -61,7 +61,7 @@ async fn main() -> Result<(), Box> { let server = EchoServer { addr }; let serve = Server::builder() - .add_service(pb::server::EchoServer::new(server)) + .add_service(pb::echo_server::EchoServer::new(server)) .serve(addr); tokio::spawn(async move { diff --git a/tonic-examples/src/multiplex/client.rs b/tonic-examples/src/multiplex/client.rs index 30f72a39c..b79903662 100644 --- a/tonic-examples/src/multiplex/client.rs +++ b/tonic-examples/src/multiplex/client.rs @@ -6,8 +6,8 @@ pub mod echo { tonic::include_proto!("grpc.examples.echo"); } -use echo::{client::EchoClient, EchoRequest}; -use hello_world::{client::GreeterClient, HelloRequest}; +use echo::{echo_client::EchoClient, EchoRequest}; +use hello_world::{greeter_client::GreeterClient, HelloRequest}; use tonic::transport::Endpoint; #[tokio::main] diff --git a/tonic-examples/src/multiplex/server.rs b/tonic-examples/src/multiplex/server.rs index a8af6e4af..597939ddc 100644 --- a/tonic-examples/src/multiplex/server.rs +++ b/tonic-examples/src/multiplex/server.rs @@ -10,12 +10,12 @@ pub mod echo { } use hello_world::{ - server::{Greeter, GreeterServer}, + greeter_server::{Greeter, GreeterServer}, HelloReply, HelloRequest, }; use echo::{ - server::{Echo, EchoServer}, + echo_server::{Echo, EchoServer}, EchoRequest, EchoResponse, }; diff --git a/tonic-examples/src/routeguide/client.rs b/tonic-examples/src/routeguide/client.rs index 66d769d89..6fa81a32d 100644 --- a/tonic-examples/src/routeguide/client.rs +++ b/tonic-examples/src/routeguide/client.rs @@ -12,7 +12,7 @@ pub mod route_guide { tonic::include_proto!("routeguide"); } -use route_guide::client::RouteGuideClient; +use route_guide::routeguide_client::RouteGuideClient; async fn print_features(client: &mut RouteGuideClient) -> Result<(), Box> { let rectangle = Rectangle { diff --git a/tonic-examples/src/routeguide/server.rs b/tonic-examples/src/routeguide/server.rs index 40d1022dc..f766957cb 100644 --- a/tonic-examples/src/routeguide/server.rs +++ b/tonic-examples/src/routeguide/server.rs @@ -14,7 +14,7 @@ pub mod routeguide { tonic::include_proto!("routeguide"); } -use routeguide::{server, Feature, Point, Rectangle, RouteNote, RouteSummary}; +use routeguide::{routeguide_server, Feature, Point, Rectangle, RouteNote, RouteSummary}; #[derive(Debug)] pub struct RouteGuide { @@ -22,7 +22,7 @@ pub struct RouteGuide { } #[tonic::async_trait] -impl server::RouteGuide for RouteGuide { +impl routeguide_server::RouteGuide for RouteGuide { async fn get_feature(&self, request: Request) -> Result, Status> { println!("GetFeature = {:?}", request); @@ -154,7 +154,7 @@ async fn main() -> Result<(), Box> { features: Arc::new(data::load()), }; - let svc = server::RouteGuideServer::new(route_guide); + let svc = routeguide_server::RouteGuideServer::new(route_guide); Server::builder().add_service(svc).serve(addr).await?; diff --git a/tonic-examples/src/tls/client.rs b/tonic-examples/src/tls/client.rs index f7060e70a..b40929e11 100644 --- a/tonic-examples/src/tls/client.rs +++ b/tonic-examples/src/tls/client.rs @@ -2,7 +2,7 @@ pub mod pb { tonic::include_proto!("/grpc.examples.echo"); } -use pb::{client::EchoClient, EchoRequest}; +use pb::{echo_client::EchoClient, EchoRequest}; use tonic::transport::{Certificate, Channel, ClientTlsConfig}; #[tokio::main] diff --git a/tonic-examples/src/tls/server.rs b/tonic-examples/src/tls/server.rs index 2643fd419..40bf6a77a 100644 --- a/tonic-examples/src/tls/server.rs +++ b/tonic-examples/src/tls/server.rs @@ -16,7 +16,7 @@ type Stream = VecDeque>; pub struct EchoServer; #[tonic::async_trait] -impl pb::server::Echo for EchoServer { +impl pb::echo_server::Echo for EchoServer { async fn unary_echo(&self, request: Request) -> EchoResult { let message = request.into_inner().message; Ok(Response::new(EchoResponse { message })) @@ -60,7 +60,7 @@ async fn main() -> Result<(), Box> { Server::builder() .tls_config(ServerTlsConfig::with_rustls().identity(identity)) - .add_service(pb::server::EchoServer::new(server)) + .add_service(pb::echo_server::EchoServer::new(server)) .serve(addr) .await?; diff --git a/tonic-examples/src/tls_client_auth/client.rs b/tonic-examples/src/tls_client_auth/client.rs index a77212a57..44806edab 100644 --- a/tonic-examples/src/tls_client_auth/client.rs +++ b/tonic-examples/src/tls_client_auth/client.rs @@ -2,7 +2,7 @@ pub mod pb { tonic::include_proto!("grpc.examples.echo"); } -use pb::{client::EchoClient, EchoRequest}; +use pb::{echo_client::EchoClient, EchoRequest}; use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity}; #[tokio::main] diff --git a/tonic-examples/src/tls_client_auth/server.rs b/tonic-examples/src/tls_client_auth/server.rs index 7ee7f2dd0..440e9c55b 100644 --- a/tonic-examples/src/tls_client_auth/server.rs +++ b/tonic-examples/src/tls_client_auth/server.rs @@ -15,7 +15,7 @@ type Stream = VecDeque>; pub struct EchoServer; #[tonic::async_trait] -impl pb::server::Echo for EchoServer { +impl pb::echo_server::Echo for EchoServer { async fn unary_echo(&self, request: Request) -> EchoResult { let message = request.into_inner().message; Ok(Response::new(EchoResponse { message })) @@ -43,7 +43,7 @@ async fn main() -> Result<(), Box> { Server::builder() .tls_config(tls) - .add_service(pb::server::EchoServer::new(server)) + .add_service(pb::echo_server::EchoServer::new(server)) .serve(addr) .await?; diff --git a/tonic-interop/src/client.rs b/tonic-interop/src/client.rs index 5aefdf1bf..97aa209ff 100644 --- a/tonic-interop/src/client.rs +++ b/tonic-interop/src/client.rs @@ -1,4 +1,7 @@ -use crate::{pb::client::*, pb::*, test_assert, TestAssertion}; +use crate::{ + pb::testservice_client::*, pb::unimplementedservice_client::*, pb::*, test_assert, + TestAssertion, +}; use futures_util::{future, stream, SinkExt, StreamExt}; use tokio::sync::mpsc; use tonic::transport::Channel; diff --git a/tonic-interop/src/server.rs b/tonic-interop/src/server.rs index 15e7145f4..e2db75440 100644 --- a/tonic-interop/src/server.rs +++ b/tonic-interop/src/server.rs @@ -5,7 +5,8 @@ use std::pin::Pin; use std::time::{Duration, Instant}; use tonic::{Code, Request, Response, Status}; -pub use pb::server::{TestServiceServer, UnimplementedServiceServer}; +pub use pb::testservice_server::TestServiceServer; +pub use pb::unimplementedservice_server::UnimplementedServiceServer; #[derive(Default, Clone)] pub struct TestService; @@ -17,7 +18,7 @@ type Stream = Pin< >; #[tonic::async_trait] -impl pb::server::TestService for TestService { +impl pb::testservice_server::TestService for TestService { async fn empty_call(&self, _request: Request) -> Result { Ok(Response::new(Empty {})) } @@ -155,7 +156,7 @@ impl pb::server::TestService for TestService { pub struct UnimplementedService; #[tonic::async_trait] -impl pb::server::UnimplementedService for UnimplementedService { +impl pb::unimplementedservice_server::UnimplementedService for UnimplementedService { async fn unimplemented_call(&self, _req: Request) -> Result { Err(Status::unimplemented("")) }