Skip to content

Commit 61555ff

Browse files
authored
feat(build): Support compiling well-known protobuf types (#522)
1 parent 49d517f commit 61555ff

File tree

9 files changed

+200
-41
lines changed

9 files changed

+200
-41
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ members = [
1414
"tests/included_service",
1515
"tests/same_name",
1616
"tests/wellknown",
17+
"tests/wellknown-compiled",
1718
"tests/extern_path/uuid",
1819
"tests/ambiguous_methods",
1920
"tests/extern_path/my_application",

tests/wellknown-compiled/Cargo.toml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "wellknown-compiled"
3+
version = "0.1.0"
4+
authors = ["Lucio Franco <[email protected]>"]
5+
edition = "2018"
6+
publish = false
7+
license = "MIT"
8+
9+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10+
11+
[lib]
12+
doctest = false
13+
14+
[dependencies]
15+
tonic = { path = "../../tonic" }
16+
prost = "0.7"
17+
18+
[build-dependencies]
19+
tonic-build = { path = "../../tonic-build" }

tests/wellknown-compiled/build.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
tonic_build::configure()
3+
.compile_well_known_types(true)
4+
.compile(&["proto/google.proto"], &["proto"])
5+
.unwrap();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
syntax = "proto3";
2+
3+
package google.protobuf;
4+
5+
import "google/protobuf/any.proto";
6+
import "google/protobuf/api.proto";
7+
import "google/protobuf/descriptor.proto";
8+
import "google/protobuf/duration.proto";
9+
import "google/protobuf/empty.proto";
10+
import "google/protobuf/field_mask.proto";
11+
import "google/protobuf/source_context.proto";
12+
import "google/protobuf/struct.proto";
13+
import "google/protobuf/timestamp.proto";
14+
import "google/protobuf/type.proto";
15+
import "google/protobuf/wrappers.proto";
16+

tests/wellknown-compiled/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pub mod google {
2+
pub mod protobuf {
3+
tonic::include_proto!("google.protobuf");
4+
}
5+
}
6+
7+
pub fn grok() {
8+
let _empty = crate::google::protobuf::Empty {};
9+
}

tonic-build/src/client.rs

+49-15
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ use quote::{format_ident, quote};
77
///
88
/// This takes some `Service` and will generate a `TokenStream` that contains
99
/// a public module with the generated client.
10-
pub fn generate<T: Service>(service: &T, emit_package: bool, proto_path: &str) -> TokenStream {
10+
pub fn generate<T: Service>(
11+
service: &T,
12+
emit_package: bool,
13+
proto_path: &str,
14+
compile_well_known_types: bool,
15+
) -> TokenStream {
1116
let service_ident = quote::format_ident!("{}Client", service.name());
1217
let client_mod = quote::format_ident!("{}_client", naive_snake_case(&service.name()));
13-
let methods = generate_methods(service, emit_package, proto_path);
18+
let methods = generate_methods(service, emit_package, proto_path, compile_well_known_types);
1419

1520
let connect = generate_connect(&service_ident);
1621
let service_doc = generate_doc_comments(service.comment());
@@ -87,7 +92,12 @@ fn generate_connect(_service_ident: &syn::Ident) -> TokenStream {
8792
TokenStream::new()
8893
}
8994

90-
fn generate_methods<T: Service>(service: &T, emit_package: bool, proto_path: &str) -> TokenStream {
95+
fn generate_methods<T: Service>(
96+
service: &T,
97+
emit_package: bool,
98+
proto_path: &str,
99+
compile_well_known_types: bool,
100+
) -> TokenStream {
91101
let mut stream = TokenStream::new();
92102
let package = if emit_package { service.package() } else { "" };
93103

@@ -103,10 +113,14 @@ fn generate_methods<T: Service>(service: &T, emit_package: bool, proto_path: &st
103113
stream.extend(generate_doc_comments(method.comment()));
104114

105115
let method = match (method.client_streaming(), method.server_streaming()) {
106-
(false, false) => generate_unary(method, proto_path, path),
107-
(false, true) => generate_server_streaming(method, proto_path, path),
108-
(true, false) => generate_client_streaming(method, proto_path, path),
109-
(true, true) => generate_streaming(method, proto_path, path),
116+
(false, false) => generate_unary(method, proto_path, compile_well_known_types, path),
117+
(false, true) => {
118+
generate_server_streaming(method, proto_path, compile_well_known_types, path)
119+
}
120+
(true, false) => {
121+
generate_client_streaming(method, proto_path, compile_well_known_types, path)
122+
}
123+
(true, true) => generate_streaming(method, proto_path, compile_well_known_types, path),
110124
};
111125

112126
stream.extend(method);
@@ -115,10 +129,15 @@ fn generate_methods<T: Service>(service: &T, emit_package: bool, proto_path: &st
115129
stream
116130
}
117131

118-
fn generate_unary<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
132+
fn generate_unary<T: Method>(
133+
method: &T,
134+
proto_path: &str,
135+
compile_well_known_types: bool,
136+
path: String,
137+
) -> TokenStream {
119138
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
120139
let ident = format_ident!("{}", method.name());
121-
let (request, response) = method.request_response_name(proto_path);
140+
let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
122141

123142
quote! {
124143
pub async fn #ident(
@@ -135,11 +154,16 @@ fn generate_unary<T: Method>(method: &T, proto_path: &str, path: String) -> Toke
135154
}
136155
}
137156

138-
fn generate_server_streaming<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
157+
fn generate_server_streaming<T: Method>(
158+
method: &T,
159+
proto_path: &str,
160+
compile_well_known_types: bool,
161+
path: String,
162+
) -> TokenStream {
139163
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
140164
let ident = format_ident!("{}", method.name());
141165

142-
let (request, response) = method.request_response_name(proto_path);
166+
let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
143167

144168
quote! {
145169
pub async fn #ident(
@@ -156,11 +180,16 @@ fn generate_server_streaming<T: Method>(method: &T, proto_path: &str, path: Stri
156180
}
157181
}
158182

159-
fn generate_client_streaming<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
183+
fn generate_client_streaming<T: Method>(
184+
method: &T,
185+
proto_path: &str,
186+
compile_well_known_types: bool,
187+
path: String,
188+
) -> TokenStream {
160189
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
161190
let ident = format_ident!("{}", method.name());
162191

163-
let (request, response) = method.request_response_name(proto_path);
192+
let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
164193

165194
quote! {
166195
pub async fn #ident(
@@ -177,11 +206,16 @@ fn generate_client_streaming<T: Method>(method: &T, proto_path: &str, path: Stri
177206
}
178207
}
179208

180-
fn generate_streaming<T: Method>(method: &T, proto_path: &str, path: String) -> TokenStream {
209+
fn generate_streaming<T: Method>(
210+
method: &T,
211+
proto_path: &str,
212+
compile_well_known_types: bool,
213+
path: String,
214+
) -> TokenStream {
181215
let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
182216
let ident = format_ident!("{}", method.name());
183217

184-
let (request, response) = method.request_response_name(proto_path);
218+
let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
185219

186220
quote! {
187221
pub async fn #ident(

tonic-build/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ pub trait Method {
147147
/// Get comments about this item.
148148
fn comment(&self) -> &[Self::Comment];
149149
/// Type name of request and response.
150-
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream);
150+
fn request_response_name(
151+
&self,
152+
proto_path: &str,
153+
compile_well_known_types: bool,
154+
) -> (TokenStream, TokenStream);
151155
}
152156

153157
/// Format files under the out_dir with rustfmt

tonic-build/src/prost.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub fn configure() -> Builder {
1818
field_attributes: Vec::new(),
1919
type_attributes: Vec::new(),
2020
proto_path: "super".to_string(),
21+
compile_well_known_types: false,
2122
#[cfg(feature = "rustfmt")]
2223
format: true,
2324
emit_package: true,
@@ -94,8 +95,13 @@ impl crate::Method for Method {
9495
&self.comments.leading[..]
9596
}
9697

97-
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream) {
98-
let request = if self.input_proto_type.starts_with(".google.protobuf")
98+
fn request_response_name(
99+
&self,
100+
proto_path: &str,
101+
compile_well_known_types: bool,
102+
) -> (TokenStream, TokenStream) {
103+
let request = if (self.input_proto_type.starts_with(".google.protobuf")
104+
&& !compile_well_known_types)
99105
|| self.input_type.starts_with("::")
100106
{
101107
self.input_type.parse::<TokenStream>().unwrap()
@@ -105,7 +111,8 @@ impl crate::Method for Method {
105111
.to_token_stream()
106112
};
107113

108-
let response = if self.output_proto_type.starts_with(".google.protobuf")
114+
let response = if (self.output_proto_type.starts_with(".google.protobuf")
115+
&& !compile_well_known_types)
109116
|| self.output_type.starts_with("::")
110117
{
111118
self.output_type.parse::<TokenStream>().unwrap()
@@ -142,6 +149,7 @@ impl prost_build::ServiceGenerator for ServiceGenerator {
142149
&service,
143150
self.builder.emit_package,
144151
&self.builder.proto_path,
152+
self.builder.compile_well_known_types,
145153
);
146154
self.servers.extend(server);
147155
}
@@ -151,6 +159,7 @@ impl prost_build::ServiceGenerator for ServiceGenerator {
151159
&service,
152160
self.builder.emit_package,
153161
&self.builder.proto_path,
162+
self.builder.compile_well_known_types,
154163
);
155164
self.clients.extend(client);
156165
}
@@ -196,6 +205,7 @@ pub struct Builder {
196205
pub(crate) type_attributes: Vec<(String, String)>,
197206
pub(crate) proto_path: String,
198207
pub(crate) emit_package: bool,
208+
pub(crate) compile_well_known_types: bool,
199209

200210
out_dir: Option<PathBuf>,
201211
#[cfg(feature = "rustfmt")]
@@ -285,6 +295,15 @@ impl Builder {
285295
self
286296
}
287297

298+
/// Enable or disable directing Prost to compile well-known protobuf types instead
299+
/// of using the already-compiled versions available in the `prost-types` crate.
300+
///
301+
/// This defaults to `false`.
302+
pub fn compile_well_known_types(mut self, compile_well_known_types: bool) -> Self {
303+
self.compile_well_known_types = compile_well_known_types;
304+
self
305+
}
306+
288307
/// Compile the .proto files and execute code generation.
289308
pub fn compile<P>(self, protos: &[P], includes: &[P]) -> io::Result<()>
290309
where
@@ -326,6 +345,9 @@ impl Builder {
326345
for (prost_path, attr) in self.type_attributes.iter() {
327346
config.type_attribute(prost_path, attr);
328347
}
348+
if self.compile_well_known_types {
349+
config.compile_well_known_types();
350+
}
329351
config.service_generator(Box::new(ServiceGenerator::new(self)));
330352

331353
config.compile_protos(protos, includes)?;

0 commit comments

Comments
 (0)