feat(build): Decouple codgen from prost (#170)
This commit is contained in:
+2
-2
@@ -103,7 +103,7 @@ name = "hyper-warp-server"
|
||||
path = "src/hyper_warp/server.rs"
|
||||
|
||||
[dependencies]
|
||||
tonic = { path = "../tonic", features = ["tls"] }
|
||||
tonic = { path = "../tonic", features = ["tls", "data-prost"] }
|
||||
prost = "0.6"
|
||||
tokio = { version = "0.2", features = ["rt-threaded", "time", "stream", "fs", "macros", "uds"] }
|
||||
futures = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||
@@ -128,4 +128,4 @@ http-body = "0.3"
|
||||
pin-project = "0.4"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = { path = "../tonic-build" }
|
||||
tonic-build = { path = "../tonic-build", features = ["prost"] }
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
tonic_build::compile_protos("proto/helloworld/helloworld.proto").unwrap();
|
||||
tonic_build::compile_protos("proto/routeguide/route_guide.proto").unwrap();
|
||||
tonic_build::compile_protos("proto/echo/echo.proto").unwrap();
|
||||
tonic_build::compile_protos("proto/google/pubsub/pubsub.proto").unwrap();
|
||||
tonic_build::prost::compile_protos("proto/helloworld/helloworld.proto").unwrap();
|
||||
tonic_build::prost::compile_protos("proto/routeguide/route_guide.proto").unwrap();
|
||||
tonic_build::prost::compile_protos("proto/echo/echo.proto").unwrap();
|
||||
tonic_build::prost::compile_protos("proto/google/pubsub/pubsub.proto").unwrap();
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ At the root of your crate, create a `build.rs` file and add the following code:
|
||||
|
||||
```rust
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tonic_build::compile_protos("proto/helloworld.proto")?;
|
||||
tonic_build::prost::compile_protos("proto/helloworld.proto")?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
@@ -199,7 +199,7 @@ Create a `build.rs` file at the root of your crate:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
tonic_build::compile_protos("proto/route_guide.proto")
|
||||
tonic_build::prost::compile_protos("proto/route_guide.proto")
|
||||
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||
}
|
||||
```
|
||||
@@ -823,7 +823,7 @@ opposed to at build time, placing the resulting modules wherever we need them.
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
tonic_build::configure()
|
||||
tonic_build::prost::configure()
|
||||
.build_client(false)
|
||||
.out_dir("another_crate/src/pb")
|
||||
.compile(&["path/my_proto.proto"], &["path"])
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ tracing-subscriber = "0.2.0-alpha"
|
||||
tracing-log = "0.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = { path = "../tonic-build" }
|
||||
tonic-build = { path = "../tonic-build", features=["prost"] }
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
let proto = "proto/grpc/testing/test.proto";
|
||||
|
||||
tonic_build::compile_protos(proto).unwrap();
|
||||
tonic_build::prost::compile_protos(proto).unwrap();
|
||||
|
||||
// prevent needing to rebuild if files (or deps) haven't changed
|
||||
println!("cargo:rerun-if-changed={}", proto);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
tonic_build::configure()
|
||||
tonic_build::prost::configure()
|
||||
.build_server(false)
|
||||
.build_client(true)
|
||||
.extern_path(".uuid", "::uuid")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
tonic_build::compile_protos("proto/includer.proto").unwrap();
|
||||
tonic_build::prost::compile_protos("proto/includer.proto").unwrap();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
tonic_build::compile_protos("proto/foo.proto").unwrap();
|
||||
tonic_build::prost::compile_protos("proto/foo.proto").unwrap();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
tonic_build::compile_protos("proto/wellknown.proto").unwrap();
|
||||
tonic_build::prost::compile_protos("proto/wellknown.proto").unwrap();
|
||||
}
|
||||
|
||||
@@ -16,15 +16,16 @@ keywords = ["rpc", "grpc", "async", "codegen", "protobuf"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
prost-build = "0.6"
|
||||
prost-build = { version = "0.6", optional = true }
|
||||
syn = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["transport", "rustfmt"]
|
||||
default = ["transport", "rustfmt", "prost"]
|
||||
rustfmt = []
|
||||
transport = []
|
||||
prost = ["prost-build"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -23,7 +23,7 @@ tonic-build = <tonic-version>
|
||||
|
||||
```rust
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tonic_build::compile_protos("proto/service.proto")?;
|
||||
tonic_build::prost::compile_protos("proto/service.proto")?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
@@ -32,7 +32,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
```rust
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tonic_build::configure()
|
||||
tonic_build::prost::configure()
|
||||
.build_server(false)
|
||||
.compile(
|
||||
&["proto/helloworld/helloworld.proto"],
|
||||
|
||||
+56
-31
@@ -1,15 +1,16 @@
|
||||
use super::schema::{Context, Method, Service};
|
||||
use crate::{generate_doc_comments, naive_snake_case};
|
||||
use proc_macro2::TokenStream;
|
||||
use prost_build::{Method, Service};
|
||||
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", naive_snake_case(&service.name));
|
||||
let methods = generate_methods(service, proto);
|
||||
/// Generate service for client
|
||||
pub fn generate<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
||||
let service_ident = quote::format_ident!("{}Client", service.name());
|
||||
let client_mod = quote::format_ident!("{}_client", naive_snake_case(&service.name()));
|
||||
let methods = generate_methods(service, context);
|
||||
|
||||
let connect = generate_connect(&service_ident);
|
||||
let service_doc = generate_doc_comments(&service.comments.leading);
|
||||
let service_doc = generate_doc_comments(service.comment());
|
||||
|
||||
quote! {
|
||||
/// Generated client implementations.
|
||||
@@ -75,22 +76,26 @@ fn generate_connect(_service_ident: &syn::Ident) -> TokenStream {
|
||||
TokenStream::new()
|
||||
}
|
||||
|
||||
fn generate_methods(service: &Service, proto: &str) -> TokenStream {
|
||||
fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
for method in &service.methods {
|
||||
for method in service.methods() {
|
||||
use super::schema::Commentable;
|
||||
|
||||
let path = format!(
|
||||
"/{}.{}/{}",
|
||||
service.package, service.proto_name, method.proto_name
|
||||
service.package(),
|
||||
service.identifier(),
|
||||
method.identifier()
|
||||
);
|
||||
|
||||
stream.extend(generate_doc_comments(&method.comments.leading));
|
||||
stream.extend(generate_doc_comments(method.comment()));
|
||||
|
||||
let method = match (method.client_streaming, method.server_streaming) {
|
||||
(false, false) => generate_unary(method, &proto, path),
|
||||
(false, true) => generate_server_streaming(method, &proto, path),
|
||||
(true, false) => generate_client_streaming(method, &proto, path),
|
||||
(true, true) => generate_streaming(method, &proto, path),
|
||||
let method = match (method.client_streaming(), method.server_streaming()) {
|
||||
(false, false) => generate_unary(method, &context, path),
|
||||
(false, true) => generate_server_streaming(method, &context, path),
|
||||
(true, false) => generate_client_streaming(method, &context, path),
|
||||
(true, true) => generate_streaming(method, &context, path),
|
||||
};
|
||||
|
||||
stream.extend(method);
|
||||
@@ -99,9 +104,14 @@ fn generate_methods(service: &Service, proto: &str) -> TokenStream {
|
||||
stream
|
||||
}
|
||||
|
||||
fn generate_unary(method: &Method, proto: &str, path: String) -> TokenStream {
|
||||
let ident = format_ident!("{}", method.name);
|
||||
let (request, response) = crate::replace_wellknown(proto, &method);
|
||||
fn generate_unary<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
context: &T::Context,
|
||||
path: String,
|
||||
) -> TokenStream {
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
let ident = format_ident!("{}", method.name());
|
||||
let (request, response) = method.request_response_name(context);
|
||||
|
||||
quote! {
|
||||
pub async fn #ident(
|
||||
@@ -111,17 +121,22 @@ fn generate_unary(method: &Method, proto: &str, path: String) -> TokenStream {
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
self.inner.unary(request.into_request(), path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_server_streaming(method: &Method, proto: &str, path: String) -> TokenStream {
|
||||
let ident = format_ident!("{}", method.name);
|
||||
fn generate_server_streaming<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
context: &T::Context,
|
||||
path: String,
|
||||
) -> TokenStream {
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
let ident = format_ident!("{}", method.name());
|
||||
|
||||
let (request, response) = crate::replace_wellknown(proto, &method);
|
||||
let (request, response) = method.request_response_name(context);
|
||||
|
||||
quote! {
|
||||
pub async fn #ident(
|
||||
@@ -131,17 +146,22 @@ fn generate_server_streaming(method: &Method, proto: &str, path: String) -> Toke
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
self.inner.server_streaming(request.into_request(), path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_client_streaming(method: &Method, proto: &str, path: String) -> TokenStream {
|
||||
let ident = format_ident!("{}", method.name);
|
||||
fn generate_client_streaming<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
context: &T::Context,
|
||||
path: String,
|
||||
) -> TokenStream {
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
let ident = format_ident!("{}", method.name());
|
||||
|
||||
let (request, response) = crate::replace_wellknown(proto, &method);
|
||||
let (request, response) = method.request_response_name(context);
|
||||
|
||||
quote! {
|
||||
pub async fn #ident(
|
||||
@@ -151,17 +171,22 @@ fn generate_client_streaming(method: &Method, proto: &str, path: String) -> Toke
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
self.inner.client_streaming(request.into_streaming_request(), path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_streaming(method: &Method, proto: &str, path: String) -> TokenStream {
|
||||
let ident = format_ident!("{}", method.name);
|
||||
fn generate_streaming<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
context: &T::Context,
|
||||
path: String,
|
||||
) -> TokenStream {
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
let ident = format_ident!("{}", method.name());
|
||||
|
||||
let (request, response) = crate::replace_wellknown(proto, &method);
|
||||
let (request, response) = method.request_response_name(context);
|
||||
|
||||
quote! {
|
||||
pub async fn #ident(
|
||||
@@ -171,7 +196,7 @@ fn generate_streaming(method: &Method, proto: &str, path: String) -> TokenStream
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
self.inner.streaming(request.into_streaming_request(), path, codec).await
|
||||
}
|
||||
|
||||
+21
-241
@@ -23,7 +23,7 @@
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! tonic_build::compile_protos("proto/service.proto")?;
|
||||
//! tonic_build::prost::compile_protos("proto/service.proto")?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
@@ -32,7 +32,7 @@
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! tonic_build::configure()
|
||||
//! tonic_build::prost::configure()
|
||||
//! .build_server(false)
|
||||
//! .compile(
|
||||
//! &["proto/helloworld/helloworld.proto"],
|
||||
@@ -57,163 +57,25 @@
|
||||
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
|
||||
|
||||
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream};
|
||||
use prost_build::{Config, Method};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use quote::TokenStreamExt;
|
||||
|
||||
/// Prost generator
|
||||
#[cfg(feature = "prost")]
|
||||
pub mod prost;
|
||||
/// Traits to describe schema
|
||||
pub mod schema;
|
||||
|
||||
#[cfg(feature = "rustfmt")]
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
mod client;
|
||||
mod server;
|
||||
|
||||
/// Service generator builder.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Builder {
|
||||
build_client: bool,
|
||||
build_server: bool,
|
||||
extern_path: Vec<(String, String)>,
|
||||
field_attributes: Vec<(String, String)>,
|
||||
type_attributes: Vec<(String, String)>,
|
||||
out_dir: Option<PathBuf>,
|
||||
#[cfg(feature = "rustfmt")]
|
||||
format: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Enable or disable gRPC client code generation.
|
||||
pub fn build_client(mut self, enable: bool) -> Self {
|
||||
self.build_client = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable or disable gRPC server code generation.
|
||||
pub fn build_server(mut self, enable: bool) -> Self {
|
||||
self.build_server = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable the output to be formated by rustfmt.
|
||||
#[cfg(feature = "rustfmt")]
|
||||
pub fn format(mut self, run: bool) -> Self {
|
||||
self.format = run;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output directory to generate code to.
|
||||
///
|
||||
/// Defaults to the `OUT_DIR` environment variable.
|
||||
pub fn out_dir(mut self, out_dir: impl AsRef<Path>) -> Self {
|
||||
self.out_dir = Some(out_dir.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
/// Declare externally provided Protobuf package or type.
|
||||
///
|
||||
/// Passed directly to `prost_build::Config.extern_path`.
|
||||
/// Note that both the Protobuf path and the rust package paths should both be fully qualified.
|
||||
/// i.e. Protobuf paths should start with "." and rust paths should start with "::"
|
||||
pub fn extern_path(mut self, proto_path: impl AsRef<str>, rust_path: impl AsRef<str>) -> Self {
|
||||
self.extern_path.push((
|
||||
proto_path.as_ref().to_string(),
|
||||
rust_path.as_ref().to_string(),
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add additional attribute to matched messages, enums, and one-offs.
|
||||
///
|
||||
/// Passed directly to `prost_build::Config.field_attribute`.
|
||||
pub fn field_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
|
||||
self.field_attributes
|
||||
.push((path.as_ref().to_string(), attribute.as_ref().to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add additional attribute to matched messages, enums, and one-offs.
|
||||
///
|
||||
/// Passed directly to `prost_build::Config.type_attribute`.
|
||||
pub fn type_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
|
||||
self.type_attributes
|
||||
.push((path.as_ref().to_string(), attribute.as_ref().to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Compile the .proto files and execute code generation.
|
||||
pub fn compile<P: AsRef<Path>>(self, protos: &[P], includes: &[P]) -> io::Result<()> {
|
||||
let mut config = Config::new();
|
||||
|
||||
#[cfg(feature = "rustfmt")]
|
||||
let format = self.format;
|
||||
|
||||
let out_dir = self
|
||||
.out_dir
|
||||
.clone()
|
||||
.unwrap_or_else(|| PathBuf::from(std::env::var("OUT_DIR").unwrap()));
|
||||
|
||||
config.out_dir(out_dir.clone());
|
||||
for (proto_path, rust_path) in self.extern_path.iter() {
|
||||
config.extern_path(proto_path, rust_path);
|
||||
}
|
||||
for (path, attr) in self.field_attributes.iter() {
|
||||
config.field_attribute(path, attr);
|
||||
}
|
||||
for (path, attr) in self.type_attributes.iter() {
|
||||
config.type_attribute(path, attr);
|
||||
}
|
||||
config.service_generator(Box::new(ServiceGenerator::new(self)));
|
||||
|
||||
config.compile_protos(protos, includes)?;
|
||||
|
||||
#[cfg(feature = "rustfmt")]
|
||||
{
|
||||
if format {
|
||||
fmt(out_dir.to_str().expect("Expected utf8 out_dir"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure tonic-build code generation.
|
||||
///
|
||||
/// Use [`compile_protos`] instead if you don't need to tweak anything.
|
||||
pub fn configure() -> Builder {
|
||||
Builder {
|
||||
build_client: true,
|
||||
build_server: true,
|
||||
out_dir: None,
|
||||
extern_path: Vec::new(),
|
||||
field_attributes: Vec::new(),
|
||||
type_attributes: Vec::new(),
|
||||
#[cfg(feature = "rustfmt")]
|
||||
format: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple `.proto` compiling. Use [`configure`] instead if you need more options.
|
||||
///
|
||||
/// The include directory will be the parent folder of the specified path.
|
||||
/// The package name will be the filename without the extension.
|
||||
pub fn compile_protos(proto_path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let proto_path: &Path = proto_path.as_ref();
|
||||
|
||||
// directory the main .proto file resides in
|
||||
let proto_dir = proto_path
|
||||
.parent()
|
||||
.expect("proto file should reside in a directory");
|
||||
|
||||
self::configure().compile(&[proto_path], &[proto_dir])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Serivce code generation for client
|
||||
pub mod client;
|
||||
/// Serivce code generation for Server
|
||||
pub mod server;
|
||||
|
||||
/// Format files under the out_dir with rustfmt
|
||||
#[cfg(feature = "rustfmt")]
|
||||
fn fmt(out_dir: &str) {
|
||||
pub fn fmt(out_dir: &str) {
|
||||
let dir = std::fs::read_dir(out_dir).unwrap();
|
||||
|
||||
for entry in dir {
|
||||
@@ -232,73 +94,13 @@ fn fmt(out_dir: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
struct ServiceGenerator {
|
||||
builder: Builder,
|
||||
clients: TokenStream,
|
||||
servers: TokenStream,
|
||||
}
|
||||
|
||||
impl ServiceGenerator {
|
||||
fn new(builder: Builder) -> Self {
|
||||
ServiceGenerator {
|
||||
builder,
|
||||
clients: TokenStream::default(),
|
||||
servers: TokenStream::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl prost_build::ServiceGenerator for ServiceGenerator {
|
||||
fn generate(&mut self, service: prost_build::Service, _buf: &mut String) {
|
||||
let path = "super";
|
||||
|
||||
if self.builder.build_server {
|
||||
let server = server::generate(&service, path);
|
||||
self.servers.extend(server);
|
||||
}
|
||||
|
||||
if self.builder.build_client {
|
||||
let client = client::generate(&service, path);
|
||||
self.clients.extend(client);
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(&mut self, buf: &mut String) {
|
||||
if self.builder.build_client && !self.clients.is_empty() {
|
||||
let clients = &self.clients;
|
||||
|
||||
let client_service = quote::quote! {
|
||||
#clients
|
||||
};
|
||||
|
||||
let code = format!("{}", client_service);
|
||||
buf.push_str(&code);
|
||||
|
||||
self.clients = TokenStream::default();
|
||||
}
|
||||
|
||||
if self.builder.build_server && !self.servers.is_empty() {
|
||||
let servers = &self.servers;
|
||||
|
||||
let server_service = quote::quote! {
|
||||
#servers
|
||||
};
|
||||
|
||||
let code = format!("{}", server_service);
|
||||
buf.push_str(&code);
|
||||
|
||||
self.servers = TokenStream::default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a singular line of a doc comment
|
||||
fn generate_doc_comment(comment: &str) -> TokenStream {
|
||||
fn generate_doc_comment<S: AsRef<str>>(comment: S) -> TokenStream {
|
||||
let mut doc_stream = TokenStream::new();
|
||||
|
||||
doc_stream.append(Ident::new("doc", Span::call_site()));
|
||||
doc_stream.append(Punct::new('=', Spacing::Alone));
|
||||
doc_stream.append(Literal::string(&comment));
|
||||
doc_stream.append(Literal::string(comment.as_ref()));
|
||||
|
||||
let group = Group::new(Delimiter::Bracket, doc_stream);
|
||||
|
||||
@@ -309,40 +111,18 @@ fn generate_doc_comment(comment: &str) -> TokenStream {
|
||||
}
|
||||
|
||||
// Generate a larger doc comment composed of many lines of doc comments
|
||||
fn generate_doc_comments<T: AsRef<str>>(comments: &[T]) -> TokenStream {
|
||||
fn generate_doc_comments<'a, T: AsRef<str> + 'a, C: IntoIterator<Item = &'a T>>(
|
||||
comments: C,
|
||||
) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
for comment in comments {
|
||||
stream.extend(generate_doc_comment(comment.as_ref()));
|
||||
stream.extend(generate_doc_comment(comment));
|
||||
}
|
||||
|
||||
stream
|
||||
}
|
||||
|
||||
fn replace_wellknown(proto_path: &str, method: &Method) -> (TokenStream, TokenStream) {
|
||||
let request = if method.input_proto_type.starts_with(".google.protobuf")
|
||||
|| method.input_type.starts_with("::")
|
||||
{
|
||||
method.input_type.parse::<TokenStream>().unwrap()
|
||||
} else {
|
||||
syn::parse_str::<syn::Path>(&format!("{}::{}", proto_path, method.input_type))
|
||||
.unwrap()
|
||||
.to_token_stream()
|
||||
};
|
||||
|
||||
let response = if method.output_proto_type.starts_with(".google.protobuf")
|
||||
|| method.output_type.starts_with("::")
|
||||
{
|
||||
method.output_type.parse::<TokenStream>().unwrap()
|
||||
} else {
|
||||
syn::parse_str::<syn::Path>(&format!("{}::{}", proto_path, method.output_type))
|
||||
.unwrap()
|
||||
.to_token_stream()
|
||||
};
|
||||
|
||||
(request, response)
|
||||
}
|
||||
|
||||
fn naive_snake_case(name: &str) -> String {
|
||||
let mut s = String::new();
|
||||
let mut it = name.chars().peekable();
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
use super::{client, schema, server};
|
||||
use proc_macro2::TokenStream;
|
||||
use prost_build::{Config, Method, Service};
|
||||
use quote::ToTokens;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
impl<'a> schema::Commentable<'a> for Service {
|
||||
type Comment = String;
|
||||
type CommentContainer = &'a Vec<Self::Comment>;
|
||||
|
||||
fn comment(&'a self) -> Self::CommentContainer {
|
||||
&self.comments.leading
|
||||
}
|
||||
}
|
||||
|
||||
/// Context data used while generate prost service
|
||||
#[derive(Debug)]
|
||||
pub struct ProstContext {
|
||||
/// relative path to proto definitions from service definitions
|
||||
pub proto_path: String,
|
||||
}
|
||||
|
||||
impl schema::Context for ProstContext {
|
||||
fn codec_name(&self) -> &str {
|
||||
"tonic::codec::ProstCodec"
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> schema::Service<'a> for Service {
|
||||
type Method = Method;
|
||||
type MethodContainer = &'a Vec<Self::Method>;
|
||||
type Context = ProstContext;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn package(&self) -> &str {
|
||||
&self.package
|
||||
}
|
||||
|
||||
fn identifier(&self) -> &str {
|
||||
&self.proto_name
|
||||
}
|
||||
|
||||
fn methods(&'a self) -> Self::MethodContainer {
|
||||
&self.methods
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> schema::Commentable<'a> for Method {
|
||||
type Comment = String;
|
||||
type CommentContainer = &'a Vec<Self::Comment>;
|
||||
|
||||
fn comment(&'a self) -> Self::CommentContainer {
|
||||
&self.comments.leading
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> schema::Method<'a> for Method {
|
||||
type Context = ProstContext;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn identifier(&self) -> &str {
|
||||
&self.proto_name
|
||||
}
|
||||
|
||||
fn client_streaming(&self) -> bool {
|
||||
self.client_streaming
|
||||
}
|
||||
|
||||
fn server_streaming(&self) -> bool {
|
||||
self.server_streaming
|
||||
}
|
||||
|
||||
fn request_response_name(&self, context: &Self::Context) -> (TokenStream, TokenStream) {
|
||||
let request = if self.input_proto_type.starts_with(".google.protobuf")
|
||||
|| self.input_type.starts_with("::")
|
||||
{
|
||||
self.input_type.parse::<TokenStream>().unwrap()
|
||||
} else {
|
||||
syn::parse_str::<syn::Path>(&format!("{}::{}", context.proto_path, self.input_type))
|
||||
.unwrap()
|
||||
.to_token_stream()
|
||||
};
|
||||
|
||||
let response = if self.output_proto_type.starts_with(".google.protobuf")
|
||||
|| self.output_type.starts_with("::")
|
||||
{
|
||||
self.output_type.parse::<TokenStream>().unwrap()
|
||||
} else {
|
||||
syn::parse_str::<syn::Path>(&format!("{}::{}", context.proto_path, self.output_type))
|
||||
.unwrap()
|
||||
.to_token_stream()
|
||||
};
|
||||
|
||||
(request, response)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compile<P: AsRef<Path>>(
|
||||
builder: Builder,
|
||||
out_dir: PathBuf,
|
||||
protos: &[P],
|
||||
includes: &[P],
|
||||
) -> std::io::Result<()> {
|
||||
let mut config = Config::new();
|
||||
|
||||
config.out_dir(out_dir);
|
||||
for (proto_path, rust_path) in builder.extern_path.iter() {
|
||||
config.extern_path(proto_path, rust_path);
|
||||
}
|
||||
for (prost_path, attr) in builder.field_attributes.iter() {
|
||||
config.field_attribute(prost_path, attr);
|
||||
}
|
||||
for (prost_path, attr) in builder.type_attributes.iter() {
|
||||
config.type_attribute(prost_path, attr);
|
||||
}
|
||||
config.service_generator(Box::new(ServiceGenerator::new(builder)));
|
||||
|
||||
config.compile_protos(protos, includes)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct ServiceGenerator {
|
||||
builder: Builder,
|
||||
clients: TokenStream,
|
||||
servers: TokenStream,
|
||||
}
|
||||
|
||||
impl ServiceGenerator {
|
||||
fn new(builder: Builder) -> Self {
|
||||
ServiceGenerator {
|
||||
builder,
|
||||
clients: TokenStream::default(),
|
||||
servers: TokenStream::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl prost_build::ServiceGenerator for ServiceGenerator {
|
||||
fn generate(&mut self, service: prost_build::Service, _buf: &mut String) {
|
||||
let context = ProstContext {
|
||||
proto_path: String::from("super"),
|
||||
};
|
||||
|
||||
if self.builder.build_server {
|
||||
let server = server::generate(&service, &context);
|
||||
self.servers.extend(server);
|
||||
}
|
||||
|
||||
if self.builder.build_client {
|
||||
let client = client::generate(&service, &context);
|
||||
self.clients.extend(client);
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(&mut self, buf: &mut String) {
|
||||
if self.builder.build_client && !self.clients.is_empty() {
|
||||
let clients = &self.clients;
|
||||
|
||||
let client_service = quote::quote! {
|
||||
#clients
|
||||
};
|
||||
|
||||
let code = format!("{}", client_service);
|
||||
buf.push_str(&code);
|
||||
|
||||
self.clients = TokenStream::default();
|
||||
}
|
||||
|
||||
if self.builder.build_server && !self.servers.is_empty() {
|
||||
let servers = &self.servers;
|
||||
|
||||
let server_service = quote::quote! {
|
||||
#servers
|
||||
};
|
||||
|
||||
let code = format!("{}", server_service);
|
||||
buf.push_str(&code);
|
||||
|
||||
self.servers = TokenStream::default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Service generator builder.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Builder {
|
||||
pub(crate) build_client: bool,
|
||||
pub(crate) build_server: bool,
|
||||
pub(crate) extern_path: Vec<(String, String)>,
|
||||
pub(crate) field_attributes: Vec<(String, String)>,
|
||||
pub(crate) type_attributes: Vec<(String, String)>,
|
||||
|
||||
out_dir: Option<PathBuf>,
|
||||
#[cfg(feature = "rustfmt")]
|
||||
format: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Enable or disable gRPC client code generation.
|
||||
pub fn build_client(mut self, enable: bool) -> Self {
|
||||
self.build_client = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable or disable gRPC server code generation.
|
||||
pub fn build_server(mut self, enable: bool) -> Self {
|
||||
self.build_server = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable the output to be formated by rustfmt.
|
||||
#[cfg(feature = "rustfmt")]
|
||||
pub fn format(mut self, run: bool) -> Self {
|
||||
self.format = run;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output directory to generate code to.
|
||||
///
|
||||
/// Defaults to the `OUT_DIR` environment variable.
|
||||
pub fn out_dir(mut self, out_dir: impl AsRef<Path>) -> Self {
|
||||
self.out_dir = Some(out_dir.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
/// Declare externally provided Protobuf package or type.
|
||||
///
|
||||
/// Passed directly to `prost_build::Config.extern_path`.
|
||||
/// Note that both the Protobuf path and the rust package paths should both be fully qualified.
|
||||
/// i.e. Protobuf paths should start with "." and rust paths should start with "::"
|
||||
pub fn extern_path(mut self, proto_path: impl AsRef<str>, rust_path: impl AsRef<str>) -> Self {
|
||||
self.extern_path.push((
|
||||
proto_path.as_ref().to_string(),
|
||||
rust_path.as_ref().to_string(),
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add additional attribute to matched messages, enums, and one-offs.
|
||||
///
|
||||
/// Passed directly to `prost_build::Config.field_attribute`.
|
||||
pub fn field_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
|
||||
self.field_attributes
|
||||
.push((path.as_ref().to_string(), attribute.as_ref().to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add additional attribute to matched messages, enums, and one-offs.
|
||||
///
|
||||
/// Passed directly to `prost_build::Config.type_attribute`.
|
||||
pub fn type_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
|
||||
self.type_attributes
|
||||
.push((path.as_ref().to_string(), attribute.as_ref().to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Compile the .proto files and execute code generation.
|
||||
pub fn compile<P: AsRef<Path>>(self, protos: &[P], includes: &[P]) -> io::Result<()> {
|
||||
let out_dir = if let Some(out_dir) = self.out_dir.as_ref() {
|
||||
out_dir.clone()
|
||||
} else {
|
||||
PathBuf::from(std::env::var("OUT_DIR").unwrap())
|
||||
};
|
||||
|
||||
#[cfg(feature = "rustfmt")]
|
||||
let format = self.format;
|
||||
|
||||
compile(self, out_dir.clone(), protos, includes)?;
|
||||
|
||||
#[cfg(feature = "rustfmt")]
|
||||
{
|
||||
if format {
|
||||
super::fmt(out_dir.to_str().expect("Expected utf8 out_dir"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure tonic-build code generation.
|
||||
///
|
||||
/// Use [`compile_protos`] instead if you don't need to tweak anything.
|
||||
pub fn configure() -> Builder {
|
||||
Builder {
|
||||
build_client: true,
|
||||
build_server: true,
|
||||
out_dir: None,
|
||||
extern_path: Vec::new(),
|
||||
field_attributes: Vec::new(),
|
||||
type_attributes: Vec::new(),
|
||||
#[cfg(feature = "rustfmt")]
|
||||
format: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple `.proto` compiling. Use [`configure`] instead if you need more options.
|
||||
///
|
||||
/// The include directory will be the parent folder of the specified path.
|
||||
/// The package name will be the filename without the extension.
|
||||
pub fn compile_protos(proto_path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let proto_path: &Path = proto_path.as_ref();
|
||||
|
||||
// directory the main .proto file resides in
|
||||
let proto_dir = proto_path
|
||||
.parent()
|
||||
.expect("proto file should reside in a directory");
|
||||
|
||||
self::configure().compile(&[proto_path], &[proto_dir])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
/// Context data used in code generation
|
||||
pub trait Context {
|
||||
/// Provide name of tonic compatibale codec
|
||||
fn codec_name(&self) -> &str;
|
||||
}
|
||||
|
||||
/// Item has comment
|
||||
pub trait Commentable<'a> {
|
||||
/// Comment type
|
||||
type Comment: AsRef<str> + 'a;
|
||||
/// Container has comments.
|
||||
type CommentContainer: IntoIterator<Item = &'a Self::Comment>;
|
||||
/// Get comments about this item
|
||||
fn comment(&'a self) -> Self::CommentContainer;
|
||||
}
|
||||
|
||||
/// Service
|
||||
pub trait Service<'a>: Commentable<'a> {
|
||||
/// Method type
|
||||
type Method: Method<'a, Context = Self::Context> + 'a;
|
||||
/// Container has methods
|
||||
type MethodContainer: IntoIterator<Item = &'a Self::Method>;
|
||||
/// Common context
|
||||
type Context: Context + 'a;
|
||||
|
||||
/// Name of service
|
||||
fn name(&self) -> &str;
|
||||
/// Package name of service
|
||||
fn package(&self) -> &str;
|
||||
/// Identifier used to generate type name
|
||||
fn identifier(&self) -> &str;
|
||||
/// Methods provided by service
|
||||
fn methods(&'a self) -> Self::MethodContainer;
|
||||
}
|
||||
|
||||
/// Method
|
||||
pub trait Method<'a>: Commentable<'a> {
|
||||
/// Common context
|
||||
type Context: Context + 'a;
|
||||
|
||||
/// Name of method
|
||||
fn name(&self) -> &str;
|
||||
/// Identifier used to generate type name
|
||||
fn identifier(&self) -> &str;
|
||||
/// Method is streamed by client
|
||||
fn client_streaming(&self) -> bool;
|
||||
/// Method is streamed by server
|
||||
fn server_streaming(&self) -> bool;
|
||||
/// Type name of request and response
|
||||
fn request_response_name(&self, context: &Self::Context) -> (TokenStream, TokenStream);
|
||||
}
|
||||
+72
-58
@@ -1,20 +1,21 @@
|
||||
use super::schema::{Commentable, Context, Method, Service};
|
||||
use crate::{generate_doc_comment, generate_doc_comments, naive_snake_case};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use prost_build::{Method, Service};
|
||||
use quote::quote;
|
||||
use syn::{Ident, Lit, LitStr};
|
||||
|
||||
pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream {
|
||||
let methods = generate_methods(&service, proto_path);
|
||||
/// Generate service for Server
|
||||
pub fn generate<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
||||
let methods = generate_methods(service, context);
|
||||
|
||||
let server_service = quote::format_ident!("{}Server", service.name);
|
||||
let server_trait = quote::format_ident!("{}", service.name);
|
||||
let server_mod = quote::format_ident!("{}_server", naive_snake_case(&service.name));
|
||||
let generated_trait = generate_trait(service, proto_path, server_trait.clone());
|
||||
let service_doc = generate_doc_comments(&service.comments.leading);
|
||||
let server_service = quote::format_ident!("{}Server", service.name());
|
||||
let server_trait = quote::format_ident!("{}", service.name());
|
||||
let server_mod = quote::format_ident!("{}_server", naive_snake_case(&service.name()));
|
||||
let generated_trait = generate_trait(service, context, server_trait.clone());
|
||||
let service_doc = generate_doc_comments(service.comment());
|
||||
|
||||
// Transport based implementations
|
||||
let path = format!("{}.{}", service.package, service.proto_name);
|
||||
let path = format!("{}.{}", service.package(), service.identifier());
|
||||
let transport = generate_transport(&server_service, &server_trait, &path);
|
||||
|
||||
quote! {
|
||||
@@ -98,11 +99,15 @@ pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_trait(service: &Service, proto_path: &str, server_trait: Ident) -> TokenStream {
|
||||
let methods = generate_trait_methods(service, proto_path);
|
||||
fn generate_trait<'a, T: Service<'a>>(
|
||||
service: &'a T,
|
||||
context: &T::Context,
|
||||
server_trait: Ident,
|
||||
) -> TokenStream {
|
||||
let methods = generate_trait_methods(service, context);
|
||||
let trait_doc = generate_doc_comment(&format!(
|
||||
"Generated trait containing gRPC methods that should be implemented for use with {}Server.",
|
||||
service.name
|
||||
service.name()
|
||||
));
|
||||
|
||||
quote! {
|
||||
@@ -114,17 +119,17 @@ fn generate_trait(service: &Service, proto_path: &str, server_trait: Ident) -> T
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
fn generate_trait_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
for method in &service.methods {
|
||||
let name = quote::format_ident!("{}", method.name);
|
||||
for method in service.methods() {
|
||||
let name = quote::format_ident!("{}", method.name());
|
||||
|
||||
let (req_message, res_message) = crate::replace_wellknown(proto_path, &method);
|
||||
let (req_message, res_message) = method.request_response_name(context);
|
||||
|
||||
let method_doc = generate_doc_comments(&method.comments.leading);
|
||||
let method_doc = generate_doc_comments(method.comment());
|
||||
|
||||
let method = match (method.client_streaming, method.server_streaming) {
|
||||
let method = match (method.client_streaming(), method.server_streaming()) {
|
||||
(false, false) => {
|
||||
quote! {
|
||||
#method_doc
|
||||
@@ -140,10 +145,10 @@ fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
}
|
||||
}
|
||||
(false, true) => {
|
||||
let stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
let stream = quote::format_ident!("{}Stream", method.identifier());
|
||||
let stream_doc = generate_doc_comment(&format!(
|
||||
"Server streaming response type for the {} method.",
|
||||
method.proto_name
|
||||
method.identifier()
|
||||
));
|
||||
|
||||
quote! {
|
||||
@@ -156,10 +161,10 @@ fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
}
|
||||
}
|
||||
(true, true) => {
|
||||
let stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
let stream = quote::format_ident!("{}Stream", method.identifier());
|
||||
let stream_doc = generate_doc_comment(&format!(
|
||||
"Server streaming response type for the {} method.",
|
||||
method.proto_name
|
||||
method.identifier()
|
||||
));
|
||||
|
||||
quote! {
|
||||
@@ -203,29 +208,31 @@ fn generate_transport(
|
||||
TokenStream::new()
|
||||
}
|
||||
|
||||
fn generate_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
for method in &service.methods {
|
||||
for method in service.methods() {
|
||||
let path = format!(
|
||||
"/{}.{}/{}",
|
||||
service.package, service.proto_name, method.proto_name
|
||||
service.package(),
|
||||
service.identifier(),
|
||||
method.identifier()
|
||||
);
|
||||
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 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),
|
||||
let method_stream = match (method.client_streaming(), method.server_streaming()) {
|
||||
(false, false) => generate_unary(method, ident, context, server_trait),
|
||||
|
||||
(false, true) => {
|
||||
generate_server_streaming(method, ident.clone(), proto_path, server_trait)
|
||||
generate_server_streaming(method, ident.clone(), context, server_trait)
|
||||
}
|
||||
(true, false) => {
|
||||
generate_client_streaming(method, ident.clone(), proto_path, server_trait)
|
||||
generate_client_streaming(method, ident.clone(), context, server_trait)
|
||||
}
|
||||
|
||||
(true, true) => generate_streaming(method, ident.clone(), proto_path, server_trait),
|
||||
(true, true) => generate_streaming(method, ident.clone(), context, server_trait),
|
||||
};
|
||||
|
||||
let method = quote! {
|
||||
@@ -239,15 +246,17 @@ fn generate_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
stream
|
||||
}
|
||||
|
||||
fn generate_unary(
|
||||
method: &Method,
|
||||
fn generate_unary<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
context: &T::Context,
|
||||
server_trait: Ident,
|
||||
) -> TokenStream {
|
||||
let service_ident = quote::format_ident!("{}Svc", method.proto_name);
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
|
||||
let (request, response) = crate::replace_wellknown(proto_path, &method);
|
||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||
|
||||
let (request, response) = method.request_response_name(context);
|
||||
|
||||
quote! {
|
||||
struct #service_ident<T: #server_trait >(pub Arc<T>);
|
||||
@@ -270,7 +279,7 @@ fn generate_unary(
|
||||
let interceptor = inner.1.clone();
|
||||
let inner = inner.0;
|
||||
let method = #service_ident(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
|
||||
let mut grpc = if let Some(interceptor) = interceptor {
|
||||
tonic::server::Grpc::with_interceptor(codec, interceptor)
|
||||
@@ -286,17 +295,19 @@ fn generate_unary(
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_server_streaming(
|
||||
method: &Method,
|
||||
fn generate_server_streaming<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
context: &T::Context,
|
||||
server_trait: Ident,
|
||||
) -> TokenStream {
|
||||
let service_ident = quote::format_ident!("{}Svc", method.proto_name);
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
|
||||
let (request, response) = crate::replace_wellknown(proto_path, &method);
|
||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||
|
||||
let response_stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
let (request, response) = method.request_response_name(context);
|
||||
|
||||
let response_stream = quote::format_ident!("{}Stream", method.identifier());
|
||||
|
||||
quote! {
|
||||
struct #service_ident<T: #server_trait >(pub Arc<T>);
|
||||
@@ -321,7 +332,7 @@ fn generate_server_streaming(
|
||||
let interceptor = inner.1;
|
||||
let inner = inner.0;
|
||||
let method = #service_ident(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
|
||||
let mut grpc = if let Some(interceptor) = interceptor {
|
||||
tonic::server::Grpc::with_interceptor(codec, interceptor)
|
||||
@@ -337,15 +348,16 @@ fn generate_server_streaming(
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_client_streaming(
|
||||
method: &Method,
|
||||
fn generate_client_streaming<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
context: &T::Context,
|
||||
server_trait: Ident,
|
||||
) -> TokenStream {
|
||||
let service_ident = quote::format_ident!("{}Svc", method.proto_name);
|
||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||
|
||||
let (request, response) = crate::replace_wellknown(proto_path, &method);
|
||||
let (request, response) = method.request_response_name(context);
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
|
||||
quote! {
|
||||
struct #service_ident<T: #server_trait >(pub Arc<T>);
|
||||
@@ -370,7 +382,7 @@ fn generate_client_streaming(
|
||||
let interceptor = inner.1;
|
||||
let inner = inner.0;
|
||||
let method = #service_ident(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
|
||||
let mut grpc = if let Some(interceptor) = interceptor {
|
||||
tonic::server::Grpc::with_interceptor(codec, interceptor)
|
||||
@@ -386,17 +398,19 @@ fn generate_client_streaming(
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_streaming(
|
||||
method: &Method,
|
||||
fn generate_streaming<'a, T: Method<'a>>(
|
||||
method: &T,
|
||||
method_ident: Ident,
|
||||
proto_path: &str,
|
||||
context: &T::Context,
|
||||
server_trait: Ident,
|
||||
) -> TokenStream {
|
||||
let service_ident = quote::format_ident!("{}Svc", method.proto_name);
|
||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
||||
|
||||
let (request, response) = crate::replace_wellknown(proto_path, &method);
|
||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||
|
||||
let response_stream = quote::format_ident!("{}Stream", method.proto_name);
|
||||
let (request, response) = method.request_response_name(context);
|
||||
|
||||
let response_stream = quote::format_ident!("{}Stream", method.identifier());
|
||||
|
||||
quote! {
|
||||
struct #service_ident<T: #server_trait>(pub Arc<T>);
|
||||
@@ -421,7 +435,7 @@ fn generate_streaming(
|
||||
let interceptor = inner.1;
|
||||
let inner = inner.0;
|
||||
let method = #service_ident(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let codec = #codec_name::default();
|
||||
|
||||
let mut grpc = if let Some(interceptor) = interceptor {
|
||||
tonic::server::Grpc::with_interceptor(codec, interceptor)
|
||||
|
||||
+3
-2
@@ -23,8 +23,8 @@ categories = ["web-programming", "network-programming", "asynchronous"]
|
||||
keywords = ["rpc", "grpc", "async", "futures", "protobuf"]
|
||||
|
||||
[features]
|
||||
default = ["transport", "codegen"]
|
||||
codegen = ["async-trait", "prost", "prost-derive"]
|
||||
default = ["transport", "codegen", "data-prost"]
|
||||
codegen = ["async-trait"]
|
||||
transport = [
|
||||
"hyper",
|
||||
"tokio",
|
||||
@@ -35,6 +35,7 @@ transport = [
|
||||
]
|
||||
tls = ["transport", "tokio-rustls"]
|
||||
tls-roots = ["tls", "rustls-native-certs"]
|
||||
data-prost = ["prost", "prost-derive"]
|
||||
|
||||
# [[bench]]
|
||||
# name = "bench_main"
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
mod buffer;
|
||||
mod decode;
|
||||
mod encode;
|
||||
#[cfg(feature = "prost")]
|
||||
#[cfg(feature = "data-prost")]
|
||||
mod prost;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
#[cfg(all(test, feature = "data-prost"))]
|
||||
mod prost_tests;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub use self::decode::Streaming;
|
||||
pub(crate) use self::encode::{encode_client, encode_server};
|
||||
#[cfg(feature = "prost")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "prost")))]
|
||||
#[cfg(feature = "data-prost")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "data-prost")))]
|
||||
pub use self::prost::ProstCodec;
|
||||
use crate::Status;
|
||||
pub use buffer::{DecodeBuf, EncodeBuf};
|
||||
|
||||
Reference in New Issue
Block a user