chore(build): Refactor codegen traits (#302)
Signed-off-by: Lucio Franco <[email protected]>
This commit is contained in:
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::prost::compile_protos("proto/helloworld/helloworld.proto").unwrap();
|
tonic_build::compile_protos("proto/helloworld/helloworld.proto").unwrap();
|
||||||
tonic_build::prost::compile_protos("proto/routeguide/route_guide.proto").unwrap();
|
tonic_build::compile_protos("proto/routeguide/route_guide.proto").unwrap();
|
||||||
tonic_build::prost::compile_protos("proto/echo/echo.proto").unwrap();
|
tonic_build::compile_protos("proto/echo/echo.proto").unwrap();
|
||||||
tonic_build::prost::compile_protos("proto/google/pubsub/pubsub.proto").unwrap();
|
tonic_build::compile_protos("proto/google/pubsub/pubsub.proto").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -823,7 +823,7 @@ opposed to at build time, placing the resulting modules wherever we need them.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::prost::configure()
|
tonic_build::configure()
|
||||||
.build_client(false)
|
.build_client(false)
|
||||||
.out_dir("another_crate/src/pb")
|
.out_dir("another_crate/src/pb")
|
||||||
.compile(&["path/my_proto.proto"], &["path"])
|
.compile(&["path/my_proto.proto"], &["path"])
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
let proto = "proto/grpc/testing/test.proto";
|
let proto = "proto/grpc/testing/test.proto";
|
||||||
|
|
||||||
tonic_build::prost::compile_protos(proto).unwrap();
|
tonic_build::compile_protos(proto).unwrap();
|
||||||
|
|
||||||
// prevent needing to rebuild if files (or deps) haven't changed
|
// prevent needing to rebuild if files (or deps) haven't changed
|
||||||
println!("cargo:rerun-if-changed={}", proto);
|
println!("cargo:rerun-if-changed={}", proto);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fn main() -> Result<(), std::io::Error> {
|
fn main() -> Result<(), std::io::Error> {
|
||||||
tonic_build::prost::configure()
|
tonic_build::configure()
|
||||||
.build_server(false)
|
.build_server(false)
|
||||||
.build_client(true)
|
.build_client(true)
|
||||||
.extern_path(".uuid", "::uuid")
|
.extern_path(".uuid", "::uuid")
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::prost::compile_protos("proto/includer.proto").unwrap();
|
tonic_build::compile_protos("proto/includer.proto").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::prost::compile_protos("proto/foo.proto").unwrap();
|
tonic_build::compile_protos("proto/foo.proto").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::prost::compile_protos("proto/wellknown.proto").unwrap();
|
tonic_build::compile_protos("proto/wellknown.proto").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ tonic-build = <tonic-version>
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
tonic_build::prost::compile_protos("proto/service.proto")?;
|
tonic_build::compile_protos("proto/service.proto")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -32,7 +32,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
tonic_build::prost::configure()
|
tonic_build::configure()
|
||||||
.build_server(false)
|
.build_server(false)
|
||||||
.compile(
|
.compile(
|
||||||
&["proto/helloworld/helloworld.proto"],
|
&["proto/helloworld/helloworld.proto"],
|
||||||
|
|||||||
+20
-36
@@ -1,13 +1,13 @@
|
|||||||
use super::schema::{Context, Method, Service};
|
use super::schema::{Method, Service};
|
||||||
use crate::{generate_doc_comments, naive_snake_case};
|
use crate::{generate_doc_comments, naive_snake_case};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
/// Generate service for client
|
/// Generate service for client
|
||||||
pub fn generate<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
pub fn generate<T: Service>(service: &T, proto_path: &str) -> TokenStream {
|
||||||
let service_ident = quote::format_ident!("{}Client", service.name());
|
let service_ident = quote::format_ident!("{}Client", service.name());
|
||||||
let client_mod = quote::format_ident!("{}_client", naive_snake_case(&service.name()));
|
let client_mod = quote::format_ident!("{}_client", naive_snake_case(&service.name()));
|
||||||
let methods = generate_methods(service, context);
|
let methods = generate_methods(service, proto_path);
|
||||||
|
|
||||||
let connect = generate_connect(&service_ident);
|
let connect = generate_connect(&service_ident);
|
||||||
let service_doc = generate_doc_comments(service.comment());
|
let service_doc = generate_doc_comments(service.comment());
|
||||||
@@ -76,7 +76,7 @@ fn generate_connect(_service_ident: &syn::Ident) -> TokenStream {
|
|||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
fn generate_methods<T: Service>(service: &T, proto_path: &str) -> TokenStream {
|
||||||
let mut stream = TokenStream::new();
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
for method in service.methods() {
|
for method in service.methods() {
|
||||||
@@ -92,10 +92,10 @@ fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) ->
|
|||||||
stream.extend(generate_doc_comments(method.comment()));
|
stream.extend(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) => generate_unary(method, &context, path),
|
(false, false) => generate_unary(method, proto_path, path),
|
||||||
(false, true) => generate_server_streaming(method, &context, path),
|
(false, true) => generate_server_streaming(method, proto_path, path),
|
||||||
(true, false) => generate_client_streaming(method, &context, path),
|
(true, false) => generate_client_streaming(method, proto_path, path),
|
||||||
(true, true) => generate_streaming(method, &context, path),
|
(true, true) => generate_streaming(method, proto_path, path),
|
||||||
};
|
};
|
||||||
|
|
||||||
stream.extend(method);
|
stream.extend(method);
|
||||||
@@ -104,14 +104,10 @@ fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) ->
|
|||||||
stream
|
stream
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_unary<'a, T: Method<'a>>(
|
fn generate_unary<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
|
||||||
method: &T,
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
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 ident = format_ident!("{}", method.name());
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
pub async fn #ident(
|
pub async fn #ident(
|
||||||
@@ -128,15 +124,11 @@ fn generate_unary<'a, T: Method<'a>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_server_streaming<'a, T: Method<'a>>(
|
fn generate_server_streaming<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
|
||||||
method: &T,
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
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 ident = format_ident!("{}", method.name());
|
||||||
|
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
pub async fn #ident(
|
pub async fn #ident(
|
||||||
@@ -153,15 +145,11 @@ fn generate_server_streaming<'a, T: Method<'a>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_client_streaming<'a, T: Method<'a>>(
|
fn generate_client_streaming<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
|
||||||
method: &T,
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
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 ident = format_ident!("{}", method.name());
|
||||||
|
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
pub async fn #ident(
|
pub async fn #ident(
|
||||||
@@ -178,15 +166,11 @@ fn generate_client_streaming<'a, T: Method<'a>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_streaming<'a, T: Method<'a>>(
|
fn generate_streaming<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
|
||||||
method: &T,
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
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 ident = format_ident!("{}", method.name());
|
||||||
|
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
pub async fn #ident(
|
pub async fn #ident(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! tonic_build::prost::compile_protos("proto/service.proto")?;
|
//! tonic_build::compile_protos("proto/service.proto")?;
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! tonic_build::prost::configure()
|
//! tonic_build::configure()
|
||||||
//! .build_server(false)
|
//! .build_server(false)
|
||||||
//! .compile(
|
//! .compile(
|
||||||
//! &["proto/helloworld/helloworld.proto"],
|
//! &["proto/helloworld/helloworld.proto"],
|
||||||
@@ -61,7 +61,11 @@ use quote::TokenStreamExt;
|
|||||||
|
|
||||||
/// Prost generator
|
/// Prost generator
|
||||||
#[cfg(feature = "prost")]
|
#[cfg(feature = "prost")]
|
||||||
pub mod prost;
|
mod prost;
|
||||||
|
|
||||||
|
#[cfg(feature = "prost")]
|
||||||
|
pub use prost::{compile_protos, configure, Builder};
|
||||||
|
|
||||||
/// Traits to describe schema
|
/// Traits to describe schema
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
|
||||||
|
|||||||
+32
-38
@@ -5,32 +5,20 @@ use quote::ToTokens;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
impl<'a> schema::Commentable<'a> for Service {
|
const PROST_CODEC_PATH: &'static str = "tonic::codec::ProstCodec";
|
||||||
|
|
||||||
|
impl schema::Commentable for Service {
|
||||||
type Comment = String;
|
type Comment = String;
|
||||||
type CommentContainer = &'a Vec<Self::Comment>;
|
|
||||||
|
|
||||||
fn comment(&'a self) -> Self::CommentContainer {
|
fn comment(&self) -> &[Self::Comment] {
|
||||||
&self.comments.leading
|
&self.comments.leading[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context data used while generate prost service
|
impl schema::Service for Service {
|
||||||
#[derive(Debug)]
|
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
|
||||||
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 Method = Method;
|
||||||
type MethodContainer = &'a Vec<Self::Method>;
|
|
||||||
type Context = ProstContext;
|
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
@@ -44,22 +32,21 @@ impl<'a> schema::Service<'a> for Service {
|
|||||||
&self.proto_name
|
&self.proto_name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn methods(&'a self) -> Self::MethodContainer {
|
fn methods(&self) -> &[Self::Method] {
|
||||||
&self.methods
|
&self.methods[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> schema::Commentable<'a> for Method {
|
impl schema::Commentable for Method {
|
||||||
type Comment = String;
|
type Comment = String;
|
||||||
type CommentContainer = &'a Vec<Self::Comment>;
|
|
||||||
|
|
||||||
fn comment(&'a self) -> Self::CommentContainer {
|
fn comment(&self) -> &[Self::Comment] {
|
||||||
&self.comments.leading
|
&self.comments.leading[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> schema::Method<'a> for Method {
|
impl schema::Method for Method {
|
||||||
type Context = ProstContext;
|
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
@@ -77,13 +64,13 @@ impl<'a> schema::Method<'a> for Method {
|
|||||||
self.server_streaming
|
self.server_streaming
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_response_name(&self, context: &Self::Context) -> (TokenStream, TokenStream) {
|
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream) {
|
||||||
let request = if self.input_proto_type.starts_with(".google.protobuf")
|
let request = if self.input_proto_type.starts_with(".google.protobuf")
|
||||||
|| self.input_type.starts_with("::")
|
|| self.input_type.starts_with("::")
|
||||||
{
|
{
|
||||||
self.input_type.parse::<TokenStream>().unwrap()
|
self.input_type.parse::<TokenStream>().unwrap()
|
||||||
} else {
|
} else {
|
||||||
syn::parse_str::<syn::Path>(&format!("{}::{}", context.proto_path, self.input_type))
|
syn::parse_str::<syn::Path>(&format!("{}::{}", proto_path, self.input_type))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_token_stream()
|
.to_token_stream()
|
||||||
};
|
};
|
||||||
@@ -93,7 +80,7 @@ impl<'a> schema::Method<'a> for Method {
|
|||||||
{
|
{
|
||||||
self.output_type.parse::<TokenStream>().unwrap()
|
self.output_type.parse::<TokenStream>().unwrap()
|
||||||
} else {
|
} else {
|
||||||
syn::parse_str::<syn::Path>(&format!("{}::{}", context.proto_path, self.output_type))
|
syn::parse_str::<syn::Path>(&format!("{}::{}", proto_path, self.output_type))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_token_stream()
|
.to_token_stream()
|
||||||
};
|
};
|
||||||
@@ -145,17 +132,13 @@ impl 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 context = ProstContext {
|
|
||||||
proto_path: String::from("super"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.builder.build_server {
|
if self.builder.build_server {
|
||||||
let server = server::generate(&service, &context);
|
let server = server::generate(&service, &self.builder.proto_path);
|
||||||
self.servers.extend(server);
|
self.servers.extend(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.builder.build_client {
|
if self.builder.build_client {
|
||||||
let client = client::generate(&service, &context);
|
let client = client::generate(&service, &self.builder.proto_path);
|
||||||
self.clients.extend(client);
|
self.clients.extend(client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,6 +180,7 @@ pub struct Builder {
|
|||||||
pub(crate) extern_path: Vec<(String, String)>,
|
pub(crate) extern_path: Vec<(String, String)>,
|
||||||
pub(crate) field_attributes: Vec<(String, String)>,
|
pub(crate) field_attributes: Vec<(String, String)>,
|
||||||
pub(crate) type_attributes: Vec<(String, String)>,
|
pub(crate) type_attributes: Vec<(String, String)>,
|
||||||
|
pub(crate) proto_path: String,
|
||||||
|
|
||||||
out_dir: Option<PathBuf>,
|
out_dir: Option<PathBuf>,
|
||||||
#[cfg(feature = "rustfmt")]
|
#[cfg(feature = "rustfmt")]
|
||||||
@@ -262,6 +246,15 @@ impl Builder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the path to where tonic will search for the Request/Response proto structs
|
||||||
|
/// live relative to the module where you call `include_proto!`.
|
||||||
|
///
|
||||||
|
/// This defaults to `super` since tonic will generate code in a module.
|
||||||
|
pub fn proto_path(mut self, proto_path: impl AsRef<str>) -> Self {
|
||||||
|
self.proto_path = proto_path.as_ref().to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Compile the .proto files and execute code generation.
|
/// Compile the .proto files and execute code generation.
|
||||||
pub fn compile<P: AsRef<Path>>(self, protos: &[P], includes: &[P]) -> io::Result<()> {
|
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() {
|
let out_dir = if let Some(out_dir) = self.out_dir.as_ref() {
|
||||||
@@ -297,6 +290,7 @@ pub fn configure() -> Builder {
|
|||||||
extern_path: Vec::new(),
|
extern_path: Vec::new(),
|
||||||
field_attributes: Vec::new(),
|
field_attributes: Vec::new(),
|
||||||
type_attributes: Vec::new(),
|
type_attributes: Vec::new(),
|
||||||
|
proto_path: "super".to_string(),
|
||||||
#[cfg(feature = "rustfmt")]
|
#[cfg(feature = "rustfmt")]
|
||||||
format: true,
|
format: true,
|
||||||
}
|
}
|
||||||
@@ -306,8 +300,8 @@ pub fn configure() -> Builder {
|
|||||||
///
|
///
|
||||||
/// The include directory will be the parent folder of the specified path.
|
/// The include directory will be the parent folder of the specified path.
|
||||||
/// The package name will be the filename without the extension.
|
/// The package name will be the filename without the extension.
|
||||||
pub fn compile_protos(proto_path: impl AsRef<Path>) -> io::Result<()> {
|
pub fn compile_protos(proto: impl AsRef<Path>) -> io::Result<()> {
|
||||||
let proto_path: &Path = proto_path.as_ref();
|
let proto_path: &Path = proto.as_ref();
|
||||||
|
|
||||||
// directory the main .proto file resides in
|
// directory the main .proto file resides in
|
||||||
let proto_dir = proto_path
|
let proto_dir = proto_path
|
||||||
|
|||||||
+13
-22
@@ -1,29 +1,20 @@
|
|||||||
use proc_macro2::TokenStream;
|
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
|
/// Item has comment
|
||||||
pub trait Commentable<'a> {
|
pub trait Commentable {
|
||||||
/// Comment type
|
/// Comment type
|
||||||
type Comment: AsRef<str> + 'a;
|
type Comment: AsRef<str>;
|
||||||
/// Container has comments.
|
|
||||||
type CommentContainer: IntoIterator<Item = &'a Self::Comment>;
|
|
||||||
/// Get comments about this item
|
/// Get comments about this item
|
||||||
fn comment(&'a self) -> Self::CommentContainer;
|
fn comment(&self) -> &[Self::Comment];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Service
|
/// Service
|
||||||
pub trait Service<'a>: Commentable<'a> {
|
pub trait Service: Commentable {
|
||||||
|
/// Path to the codec
|
||||||
|
const CODEC_PATH: &'static str;
|
||||||
|
|
||||||
/// Method type
|
/// Method type
|
||||||
type Method: Method<'a, Context = Self::Context> + 'a;
|
type Method: Method;
|
||||||
/// Container has methods
|
|
||||||
type MethodContainer: IntoIterator<Item = &'a Self::Method>;
|
|
||||||
/// Common context
|
|
||||||
type Context: Context + 'a;
|
|
||||||
|
|
||||||
/// Name of service
|
/// Name of service
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
@@ -32,13 +23,13 @@ pub trait Service<'a>: Commentable<'a> {
|
|||||||
/// Identifier used to generate type name
|
/// Identifier used to generate type name
|
||||||
fn identifier(&self) -> &str;
|
fn identifier(&self) -> &str;
|
||||||
/// Methods provided by service
|
/// Methods provided by service
|
||||||
fn methods(&'a self) -> Self::MethodContainer;
|
fn methods(&self) -> &[Self::Method];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method
|
/// Method
|
||||||
pub trait Method<'a>: Commentable<'a> {
|
pub trait Method: Commentable {
|
||||||
/// Common context
|
/// Path to the codec
|
||||||
type Context: Context + 'a;
|
const CODEC_PATH: &'static str;
|
||||||
|
|
||||||
/// Name of method
|
/// Name of method
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
@@ -49,5 +40,5 @@ pub trait Method<'a>: Commentable<'a> {
|
|||||||
/// Method is streamed by server
|
/// Method is streamed by server
|
||||||
fn server_streaming(&self) -> bool;
|
fn server_streaming(&self) -> bool;
|
||||||
/// Type name of request and response
|
/// Type name of request and response
|
||||||
fn request_response_name(&self, context: &Self::Context) -> (TokenStream, TokenStream);
|
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream);
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-33
@@ -1,17 +1,17 @@
|
|||||||
use super::schema::{Commentable, Context, Method, Service};
|
use super::schema::{Commentable, Method, Service};
|
||||||
use crate::{generate_doc_comment, generate_doc_comments, naive_snake_case};
|
use crate::{generate_doc_comment, generate_doc_comments, naive_snake_case};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Ident, Lit, LitStr};
|
use syn::{Ident, Lit, LitStr};
|
||||||
|
|
||||||
/// Generate service for Server
|
/// Generate service for Server
|
||||||
pub fn generate<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
pub fn generate<T: Service>(service: &T, proto_path: &str) -> TokenStream {
|
||||||
let methods = generate_methods(service, context);
|
let methods = generate_methods(service, proto_path);
|
||||||
|
|
||||||
let server_service = quote::format_ident!("{}Server", service.name());
|
let server_service = quote::format_ident!("{}Server", service.name());
|
||||||
let server_trait = quote::format_ident!("{}", service.name());
|
let server_trait = quote::format_ident!("{}", service.name());
|
||||||
let server_mod = quote::format_ident!("{}_server", naive_snake_case(&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 generated_trait = generate_trait(service, proto_path, server_trait.clone());
|
||||||
let service_doc = generate_doc_comments(service.comment());
|
let service_doc = generate_doc_comments(service.comment());
|
||||||
|
|
||||||
// Transport based implementations
|
// Transport based implementations
|
||||||
@@ -104,12 +104,8 @@ pub fn generate<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> Tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_trait<'a, T: Service<'a>>(
|
fn generate_trait<T: Service>(service: &T, proto_path: &str, server_trait: Ident) -> TokenStream {
|
||||||
service: &'a T,
|
let methods = generate_trait_methods(service, proto_path);
|
||||||
context: &T::Context,
|
|
||||||
server_trait: Ident,
|
|
||||||
) -> TokenStream {
|
|
||||||
let methods = generate_trait_methods(service, context);
|
|
||||||
let trait_doc = generate_doc_comment(&format!(
|
let trait_doc = generate_doc_comment(&format!(
|
||||||
"Generated trait containing gRPC methods that should be implemented for use with {}Server.",
|
"Generated trait containing gRPC methods that should be implemented for use with {}Server.",
|
||||||
service.name()
|
service.name()
|
||||||
@@ -124,13 +120,13 @@ fn generate_trait<'a, T: Service<'a>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_trait_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
fn generate_trait_methods<T: Service>(service: &T, proto_path: &str) -> TokenStream {
|
||||||
let mut stream = TokenStream::new();
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
for method in service.methods() {
|
for method in service.methods() {
|
||||||
let name = quote::format_ident!("{}", method.name());
|
let name = quote::format_ident!("{}", method.name());
|
||||||
|
|
||||||
let (req_message, res_message) = method.request_response_name(context);
|
let (req_message, res_message) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
let method_doc = generate_doc_comments(method.comment());
|
let method_doc = generate_doc_comments(method.comment());
|
||||||
|
|
||||||
@@ -213,7 +209,7 @@ fn generate_transport(
|
|||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) -> TokenStream {
|
fn generate_methods<T: Service>(service: &T, proto_path: &str) -> TokenStream {
|
||||||
let mut stream = TokenStream::new();
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
for method in service.methods() {
|
for method in service.methods() {
|
||||||
@@ -228,16 +224,16 @@ fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) ->
|
|||||||
let server_trait = quote::format_ident!("{}", service.name());
|
let server_trait = quote::format_ident!("{}", service.name());
|
||||||
|
|
||||||
let method_stream = match (method.client_streaming(), method.server_streaming()) {
|
let method_stream = match (method.client_streaming(), method.server_streaming()) {
|
||||||
(false, false) => generate_unary(method, ident, context, server_trait),
|
(false, false) => generate_unary(method, proto_path, ident, server_trait),
|
||||||
|
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
generate_server_streaming(method, ident.clone(), context, server_trait)
|
generate_server_streaming(method, proto_path, ident.clone(), server_trait)
|
||||||
}
|
}
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
generate_client_streaming(method, ident.clone(), context, server_trait)
|
generate_client_streaming(method, proto_path, ident.clone(), server_trait)
|
||||||
}
|
}
|
||||||
|
|
||||||
(true, true) => generate_streaming(method, ident.clone(), context, server_trait),
|
(true, true) => generate_streaming(method, proto_path, ident.clone(), server_trait),
|
||||||
};
|
};
|
||||||
|
|
||||||
let method = quote! {
|
let method = quote! {
|
||||||
@@ -251,17 +247,17 @@ fn generate_methods<'a, T: Service<'a>>(service: &'a T, context: &T::Context) ->
|
|||||||
stream
|
stream
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_unary<'a, T: Method<'a>>(
|
fn generate_unary<T: Method>(
|
||||||
method: &T,
|
method: &T,
|
||||||
|
proto_path: &str,
|
||||||
method_ident: Ident,
|
method_ident: Ident,
|
||||||
context: &T::Context,
|
|
||||||
server_trait: Ident,
|
server_trait: Ident,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
|
|
||||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||||
|
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@@ -301,17 +297,17 @@ fn generate_unary<'a, T: Method<'a>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_server_streaming<'a, T: Method<'a>>(
|
fn generate_server_streaming<T: Method>(
|
||||||
method: &T,
|
method: &T,
|
||||||
|
proto_path: &str,
|
||||||
method_ident: Ident,
|
method_ident: Ident,
|
||||||
context: &T::Context,
|
|
||||||
server_trait: Ident,
|
server_trait: Ident,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
|
|
||||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||||
|
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
let response_stream = quote::format_ident!("{}Stream", method.identifier());
|
let response_stream = quote::format_ident!("{}Stream", method.identifier());
|
||||||
|
|
||||||
@@ -355,16 +351,16 @@ fn generate_server_streaming<'a, T: Method<'a>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_client_streaming<'a, T: Method<'a>>(
|
fn generate_client_streaming<T: Method>(
|
||||||
method: &T,
|
method: &T,
|
||||||
|
proto_path: &str,
|
||||||
method_ident: Ident,
|
method_ident: Ident,
|
||||||
context: &T::Context,
|
|
||||||
server_trait: Ident,
|
server_trait: Ident,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||||
|
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@@ -406,17 +402,17 @@ fn generate_client_streaming<'a, T: Method<'a>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_streaming<'a, T: Method<'a>>(
|
fn generate_streaming<T: Method>(
|
||||||
method: &T,
|
method: &T,
|
||||||
|
proto_path: &str,
|
||||||
method_ident: Ident,
|
method_ident: Ident,
|
||||||
context: &T::Context,
|
|
||||||
server_trait: Ident,
|
server_trait: Ident,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let codec_name = syn::parse_str::<syn::Path>(context.codec_name()).unwrap();
|
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
|
||||||
|
|
||||||
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
let service_ident = quote::format_ident!("{}Svc", method.identifier());
|
||||||
|
|
||||||
let (request, response) = method.request_response_name(context);
|
let (request, response) = method.request_response_name(proto_path);
|
||||||
|
|
||||||
let response_stream = quote::format_ident!("{}Stream", method.identifier());
|
let response_stream = quote::format_ident!("{}Stream", method.identifier());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user