Repository: hyperium/tonic (rust)

Issue:
Panic while building in tonic-build/src/service.rs:219
### Version

git master
2734d3af28f1569d1db851660861112fa449fee8

### Platform

`Linux RILEY-LT 4.19.43-microsoft-standard #1 SMP Mon May 20 19:35:22 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux`

### Crates

`tonic-build`

### Description
```
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("expected identifier")', src/libcore/result.rs:1165:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:76
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:60
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1030
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1412
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:64
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:196
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:210
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:477
  11: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:380
  12: rust_begin_unwind
             at src/libstd/panicking.rs:307
  13: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
  14: core::result::unwrap_failed
             at src/libcore/result.rs:1165
  15: core::result::Result<T,E>::unwrap
             at /rustc/5752b6348ee6971573b278c315a02193c847ee32/src/libcore/result.rs:933
  16: tonic_build::service::generate_unary
             at /home/riley/.cargo/git/checkouts/tonic-54a04bc36763b24d/8f80d0f/tonic-build/src/service.rs:219
  17: tonic_build::service::generate_methods
             at /home/riley/.cargo/git/checkouts/tonic-54a04bc36763b24d/8f80d0f/tonic-build/src/service.rs:186
  18: tonic_build::service::generate
             at /home/riley/.cargo/git/checkouts/tonic-54a04bc36763b24d/8f80d0f/tonic-build/src/service.rs:8
  19: <tonic_build::ServiceGenerator as prost_build::ServiceGenerator>::generate
             at /home/riley/.cargo/git/checkouts/tonic-54a04bc36763b24d/8f80d0f/tonic-build/src/lib.rs:176
  20: prost_build::code_generator::CodeGenerator::push_service::{{closure}}
             at /home/riley/.cargo/registry/src/github.com-1ecc6299db9ec823/prost-build-0.5.0/src/code_generator.rs:685
  21: core::option::Option<T>::map
             at /rustc/5752b6348ee6971573b278c315a02193c847ee32/src/libcore/option.rs:447
  22: prost_build::code_generator::CodeGenerator::push_service
             at /home/riley/.cargo/registry/src/github.com-1ecc6299db9ec823/prost-build-0.5.0/src/code_generator.rs:682
  23: prost_build::code_generator::CodeGenerator::generate
             at /home/riley/.cargo/registry/src/github.com-1ecc6299db9ec823/prost-build-0.5.0/src/code_generator.rs:103
  24: prost_build::Config::generate
             at /home/riley/.cargo/registry/src/github.com-1ecc6299db9ec823/prost-build-0.5.0/src/lib.rs:566
  25: prost_build::Config::compile_protos
             at /home/riley/.cargo/registry/src/github.com-1ecc6299db9ec823/prost-build-0.5.0/src/lib.rs:543
  26: tonic_build::Builder::compile
             at /home/riley/.cargo/git/checkouts/tonic-54a04bc36763b24d/8f80d0f/tonic-build/src/lib.rs:98
  27: tonic_build::compile_protos
             at /home/riley/.cargo/git/checkouts/tonic-54a04bc36763b24d/8f80d0f/tonic-build/src/lib.rs:130
  28: build_script_build::main
             at rlbi_proto/build.rs:2
  29: std::rt::lang_start::{{closure}}
             at /rustc/5752b6348ee6971573b278c315a02193c847ee32/src/libstd/rt.rs:64
  30: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:49
  31: std::panicking::try::do_call
             at src/libstd/panicking.rs:292
  32: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:80
  33: std::panicking::try
             at src/libstd/panicking.rs:271
  34: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  35: std::rt::lang_start_internal
             at src/libstd/rt.rs:48
  36: std::rt::lang_start
             at /rustc/5752b6348ee6971573b278c315a02193c847ee32/src/libstd/rt.rs:64
  37: main
  38: __libc_start_main
  39: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```


admin.proto:
```
syntax = "proto3";

package rlbi.admin.v1;

import "google/protobuf/empty.proto";

option java_package = "rlbi.admin.v1";
option java_outer_classname = "AdminProto";
option optimize_for = SPEED;

service Admin {
  rpc AddApplication(AddApplicationRequest) returns (google.protobuf.Empty);

  rpc RenameApplication(RenameApplicationRequest) returns (google.protobuf.Empty);
}

message AddApplicationRequest {
  string name = 1;
}

message RenameApplicationRequest {
    string old_name = 1;
    string new_name = 2;
  }
```

build.rs:
```
fn main() {
    tonic_build::compile_protos("proto/rlbi/admin/v1/admin.proto").unwrap();
}
```

Cargo.toml:
```
[package]
name = "rlbi_proto"
version = "0.1.0"
authors = ["Riley Labrecque"]
edition = "2018"

[dependencies]
tonic               = { git = "https://github.com/hyperium/tonic", features = ["rustls"] }
prost               = "0.5"
bytes               = "0.4"

[build-dependencies]
tonic-build = { git = "https://github.com/hyperium/tonic" }
```

Note: Compiling some other protos works fine, so it's something about mine, but I haven't quite been able to figure it out yet. Maybe the import or usage of the common types?

Pull request:
fix(codegen): Fix Empty protobuf type and add unimplemented
This also includes a few release items.

Closes #25 


Patch:
diff --git a/tonic-build/Cargo.toml b/tonic-build/Cargo.toml
index a5a85a5..d048c34 100644
--- a/tonic-build/Cargo.toml
+++ b/tonic-build/Cargo.toml
@@ -3,6 +3,7 @@ name = "tonic-build"
 version = "0.1.0-alpha.1"
 authors = ["Lucio Franco <luciofranco14@gmail.com>"]
 edition = "2018"
+license = "MIT"
 documentation = "https://docs.rs/tonic/0.1.0-alpha.1/tonic/"
 repository = "https://github.com/hyperium/tonic"
 homepage = "https://github.com/hyperium/tonic"
diff --git a/tonic-build/src/client.rs b/tonic-build/src/client.rs
index 9d9c044..63ef325 100644
--- a/tonic-build/src/client.rs
+++ b/tonic-build/src/client.rs
@@ -2,7 +2,6 @@ use crate::generate_doc_comments;
 use proc_macro2::TokenStream;
 use prost_build::{Method, Service};
 use quote::{format_ident, quote};
-use syn::Path;
 
 pub(crate) fn generate(service: &Service, proto: &str) -> TokenStream {
     let service_ident = quote::format_ident!("{}Client", service.name);
@@ -97,8 +96,8 @@ fn generate_methods(service: &Service, proto: &str) -> TokenStream {
 
 fn generate_unary(method: &Method, proto: &str, path: String) -> TokenStream {
     let ident = format_ident!("{}", method.name);
-    let request: Path = syn::parse_str(&format!("{}::{}", proto, method.input_type)).unwrap();
-    let response: Path = syn::parse_str(&format!("{}::{}", proto, method.output_type)).unwrap();
+    let request = crate::replace_wellknown(proto, &method.input_type);
+    let response = crate::replace_wellknown(proto, &method.output_type);
 
     quote! {
         pub async fn #ident(&mut self, request: tonic::Request<#request>)
@@ -113,8 +112,9 @@ fn generate_unary(method: &Method, proto: &str, path: String) -> TokenStream {
 
 fn generate_server_streaming(method: &Method, proto: &str, path: String) -> TokenStream {
     let ident = format_ident!("{}", method.name);
-    let request: Path = syn::parse_str(&format!("{}::{}", proto, method.input_type)).unwrap();
-    let response: Path = syn::parse_str(&format!("{}::{}", proto, method.output_type)).unwrap();
+
+    let request = crate::replace_wellknown(proto, &method.input_type);
+    let response = crate::replace_wellknown(proto, &method.output_type);
 
     quote! {
         pub async fn #ident(&mut self, request: tonic::Request<#request>)
@@ -129,8 +129,9 @@ fn generate_server_streaming(method: &Method, proto: &str, path: String) -> Toke
 
 fn generate_client_streaming(method: &Method, proto: &str, path: String) -> TokenStream {
     let ident = format_ident!("{}", method.name);
-    let request: Path = syn::parse_str(&format!("{}::{}", proto, method.input_type)).unwrap();
-    let response: Path = syn::parse_str(&format!("{}::{}", proto, method.output_type)).unwrap();
+
+    let request = crate::replace_wellknown(proto, &method.input_type);
+    let response = crate::replace_wellknown(proto, &method.output_type);
 
     quote! {
         pub async fn #ident<S>(&mut self, request: tonic::Request<S>)
@@ -147,8 +148,9 @@ fn generate_client_streaming(method: &Method, proto: &str, path: String) -> Toke
 
 fn generate_streaming(method: &Method, proto: &str, path: String) -> TokenStream {
     let ident = format_ident!("{}", method.name);
-    let request: Path = syn::parse_str(&format!("{}::{}", proto, method.input_type)).unwrap();
-    let response: Path = syn::parse_str(&format!("{}::{}", proto, method.output_type)).unwrap();
+
+    let request = crate::replace_wellknown(proto, &method.input_type);
+    let response = crate::replace_wellknown(proto, &method.output_type);
 
     quote! {
         pub async fn #ident<S>(&mut self, request: tonic::Request<S>)
diff --git a/tonic-build/src/lib.rs b/tonic-build/src/lib.rs
index 696c933..50def0a 100644
--- a/tonic-build/src/lib.rs
+++ b/tonic-build/src/lib.rs
@@ -59,7 +59,7 @@
 
 use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream};
 use prost_build::Config;
-use quote::TokenStreamExt;
+use quote::{ToTokens, TokenStreamExt};
 
 #[cfg(feature = "rustfmt")]
 use std::process::Command;
@@ -77,6 +77,8 @@ pub struct Builder {
     build_client: bool,
     build_server: bool,
     out_dir: Option<PathBuf>,
+    #[cfg(feature = "rustfmt")]
+    format: bool,
 }
 
 impl Builder {
@@ -92,6 +94,13 @@ impl Builder {
         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.
@@ -104,6 +113,9 @@ impl Builder {
     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()
@@ -114,7 +126,11 @@ impl Builder {
         config.compile_protos(protos, includes)?;
 
         #[cfg(feature = "rustfmt")]
-        fmt(out_dir.to_str().expect("Expected utf8 out_dir"));
+        {
+            if format {
+                fmt(out_dir.to_str().expect("Expected utf8 out_dir"));
+            }
+        }
 
         Ok(())
     }
@@ -128,6 +144,8 @@ pub fn configure() -> Builder {
         build_client: true,
         build_server: true,
         out_dir: None,
+        #[cfg(feature = "rustfmt")]
+        format: true,
     }
 }
 
@@ -262,3 +280,14 @@ fn generate_doc_comments<T: AsRef<str>>(comments: &[T]) -> TokenStream {
 
     stream
 }
+
+fn replace_wellknown(proto_path: &str, output: &str) -> TokenStream {
+    // TODO: detect more wellknown protobuf types
+    // https://github.com/danburkert/prost/blob/master/prost-types/src/protobuf.rs
+    match output {
+        "()" => quote::quote! { () },
+        _ => syn::parse_str::<syn::Path>(&format!("{}::{}", proto_path, output))
+            .unwrap()
+            .to_token_stream(),
+    }
+}
diff --git a/tonic-build/src/service.rs b/tonic-build/src/service.rs
index 2b0e5b4..d18a642 100644
--- a/tonic-build/src/service.rs
+++ b/tonic-build/src/service.rs
@@ -2,7 +2,7 @@ use crate::{generate_doc_comment, generate_doc_comments};
 use proc_macro2::{Span, TokenStream};
 use prost_build::{Method, Service};
 use quote::quote;
-use syn::{Ident, Lit, LitStr, Path};
+use syn::{Ident, Lit, LitStr};
 
 pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream {
     let methods = generate_methods(&service, proto_path);
@@ -79,8 +79,13 @@ pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream {
                 match req.uri().path() {
                     #methods
 
-                    // TODO: implement grpc unimplemented for server
-                    _ => unimplemented!(),
+                    _ => Box::pin(async move {
+                         Ok(http::Response::builder()
+                            .status(200)
+                            .header("grpc-status", "12")
+                            .body(tonic::body::BoxBody::empty())
+                            .unwrap())
+                    }),
                 }
             }
         }
@@ -108,10 +113,9 @@ fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
 
     for method in &service.methods {
         let name = quote::format_ident!("{}", method.name);
-        let req_message: Path =
-            syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
-        let res_message: Path =
-            syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
+
+        let req_message = crate::replace_wellknown(proto_path, &method.input_type);
+        let res_message = crate::replace_wellknown(proto_path, &method.output_type);
 
         let method_doc = generate_doc_comments(&method.comments.leading);
 
@@ -214,9 +218,8 @@ fn generate_unary(
 ) -> TokenStream {
     let service_ident = Ident::new(&method.proto_name, Span::call_site());
 
-    let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
-    let response: Path =
-        syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
+    let request = crate::replace_wellknown(proto_path, &method.input_type);
+    let response = crate::replace_wellknown(proto_path, &method.output_type);
 
     quote! {
         struct #service_ident<T: #server_trait >(pub Arc<T>);
@@ -255,9 +258,8 @@ fn generate_server_streaming(
 ) -> TokenStream {
     let service_ident = Ident::new(&method.proto_name, Span::call_site());
 
-    let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
-    let response: Path =
-        syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
+    let request = crate::replace_wellknown(proto_path, &method.input_type);
+    let response = crate::replace_wellknown(proto_path, &method.output_type);
 
     let response_stream = quote::format_ident!("{}Stream", method.proto_name);
 
@@ -300,9 +302,8 @@ fn generate_client_streaming(
 ) -> TokenStream {
     let service_ident = Ident::new(&method.proto_name, Span::call_site());
 
-    let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
-    let response: Path =
-        syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
+    let request = crate::replace_wellknown(proto_path, &method.input_type);
+    let response = crate::replace_wellknown(proto_path, &method.output_type);
 
     quote! {
         struct #service_ident<T: #server_trait >(pub Arc<T>);
@@ -343,9 +344,8 @@ fn generate_streaming(
 ) -> TokenStream {
     let service_ident = Ident::new(&method.proto_name, Span::call_site());
 
-    let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap();
-    let response: Path =
-        syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap();
+    let request = crate::replace_wellknown(proto_path, &method.input_type);
+    let response = crate::replace_wellknown(proto_path, &method.output_type);
 
     let response_stream = quote::format_ident!("{}Stream", method.proto_name);
 


Test patch:
diff --git a/tonic-build/tests/empty.rs b/tonic-build/tests/empty.rs
new file mode 100644
index 0000000..5e644cd
--- /dev/null
+++ b/tonic-build/tests/empty.rs
@@ -0,0 +1,9 @@
+#[test]
+fn empty() {
+    let tmp = std::env::temp_dir();
+    tonic_build::configure()
+        .out_dir(tmp)
+        .format(false)
+        .compile(&["tests/protos/empty.proto"], &["tests/protos"])
+        .unwrap();
+}
diff --git a/tonic-build/tests/protos/empty.proto b/tonic-build/tests/protos/empty.proto
new file mode 100644
index 0000000..0d5532c
--- /dev/null
+++ b/tonic-build/tests/protos/empty.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+package empty;
+import "google/protobuf/empty.proto";
+
+service Admin {
+  rpc EmptyCall(google.protobuf.Empty) returns (google.protobuf.Empty);
+}