Initial import of grpcio-compiler. am: 166a6f003e

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/grpcio-compiler/+/1486596

Change-Id: I617616d0656fafb2d4e513f297f1ba6fc1762225
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..90abfbc
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "19a53b5cc9612511dabb365dae05286e807668e5"
+  }
+}
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..ce8033e
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,340 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "anyhow"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
+
+[[package]]
+name = "autocfg"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
+
+[[package]]
+name = "bytes"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "derive-new"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "either"
+version = "1.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+
+[[package]]
+name = "fixedbitset"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
+
+[[package]]
+name = "getrandom"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "grpcio-compiler"
+version = "0.6.0"
+dependencies = [
+ "derive-new",
+ "prost",
+ "prost-build",
+ "prost-types",
+ "protobuf",
+ "tempfile",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "itertools"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
+
+[[package]]
+name = "log"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "multimap"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce"
+
+[[package]]
+name = "petgraph"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "prost"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26"
+dependencies = [
+ "bytes",
+ "heck",
+ "itertools",
+ "log",
+ "multimap",
+ "petgraph",
+ "prost",
+ "prost-types",
+ "tempfile",
+ "which",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa"
+dependencies = [
+ "bytes",
+ "prost",
+]
+
+[[package]]
+name = "protobuf"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485"
+
+[[package]]
+name = "quote"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom",
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "which"
+version = "3.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..cecb7aa
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,56 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "grpcio-compiler"
+version = "0.6.0"
+authors = ["The TiKV Project Developers"]
+description = "gRPC compiler for grpcio"
+homepage = "https://github.com/tikv/grpc-rs"
+documentation = "https://docs.rs/grpcio-compiler"
+keywords = ["compiler", "grpc", "protobuf"]
+categories = ["network-programming"]
+license = "Apache-2.0"
+repository = "https://github.com/tikv/grpc-rs"
+
+[[bin]]
+name = "grpc_rust_plugin"
+required-features = ["protobuf-codec"]
+[dependencies.derive-new]
+version = "0.5"
+optional = true
+
+[dependencies.prost]
+version = "0.6"
+optional = true
+
+[dependencies.prost-build]
+version = "0.6"
+optional = true
+
+[dependencies.prost-types]
+version = "0.6"
+optional = true
+
+[dependencies.protobuf]
+version = "2"
+optional = true
+
+[dependencies.tempfile]
+version = "3.0"
+optional = true
+
+[features]
+default = ["protobuf-codec"]
+prost-codec = ["prost-build", "prost-types", "prost", "derive-new", "tempfile"]
+protobuf-codec = ["protobuf"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..07731e0
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,29 @@
+[package]
+name = "grpcio-compiler"
+version = "0.6.0"
+edition = "2018"
+authors = ["The TiKV Project Developers"]
+license = "Apache-2.0"
+keywords = ["compiler", "grpc", "protobuf"]
+repository = "https://github.com/tikv/grpc-rs"
+homepage = "https://github.com/tikv/grpc-rs"
+documentation = "https://docs.rs/grpcio-compiler"
+description = "gRPC compiler for grpcio"
+categories = ["network-programming"]
+
+[features]
+default = ["protobuf-codec"]
+protobuf-codec = ["protobuf"]
+prost-codec = ["prost-build", "prost-types", "prost", "derive-new", "tempfile"]
+
+[dependencies]
+protobuf = { version = "2", optional = true }
+prost = { version = "0.6", optional = true }
+prost-build = { version = "0.6", optional = true }
+prost-types = { version = "0.6", optional = true }
+derive-new = { version = "0.5", optional = true }
+tempfile = { version = "3.0", optional = true }
+
+[[bin]]
+name = "grpc_rust_plugin"
+required-features = ["protobuf-codec"]
diff --git a/src/bin/grpc_rust_plugin.rs b/src/bin/grpc_rust_plugin.rs
new file mode 100644
index 0000000..7d46238
--- /dev/null
+++ b/src/bin/grpc_rust_plugin.rs
@@ -0,0 +1,30 @@
+// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
+
+// Copyright (c) 2016, Stepan Koltsov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+extern crate grpcio_compiler;
+
+use grpcio_compiler::codegen;
+
+fn main() {
+    codegen::protoc_gen_grpc_rust_main();
+}
diff --git a/src/codegen.rs b/src/codegen.rs
new file mode 100644
index 0000000..13a6a21
--- /dev/null
+++ b/src/codegen.rs
@@ -0,0 +1,708 @@
+// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
+
+// Copyright (c) 2016, Stepan Koltsov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+use std::collections::HashMap;
+use std::io::Write;
+
+use protobuf::compiler_plugin;
+use protobuf::descriptor::*;
+use protobuf::descriptorx::*;
+
+struct CodeWriter<'a> {
+    writer: &'a mut (dyn Write + 'a),
+    indent: String,
+}
+
+impl<'a> CodeWriter<'a> {
+    pub fn new(writer: &'a mut dyn Write) -> CodeWriter<'a> {
+        CodeWriter {
+            writer,
+            indent: "".to_string(),
+        }
+    }
+
+    pub fn write_line<S: AsRef<str>>(&mut self, line: S) {
+        (if line.as_ref().is_empty() {
+            self.writer.write_all(b"\n")
+        } else {
+            let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
+            self.writer.write_all(s.as_bytes())
+        })
+        .unwrap();
+    }
+
+    pub fn write_generated(&mut self) {
+        self.write_line("// This file is generated. Do not edit");
+        self.write_generated_common();
+    }
+
+    fn write_generated_common(&mut self) {
+        // https://secure.phabricator.com/T784
+        self.write_line("// @generated");
+
+        self.write_line("");
+        self.comment("https://github.com/Manishearth/rust-clippy/issues/702");
+        self.write_line("#![allow(unknown_lints)]");
+        self.write_line("#![allow(clippy::all)]");
+        self.write_line("");
+        self.write_line("#![cfg_attr(rustfmt, rustfmt_skip)]");
+        self.write_line("");
+        self.write_line("#![allow(box_pointers)]");
+        self.write_line("#![allow(dead_code)]");
+        self.write_line("#![allow(missing_docs)]");
+        self.write_line("#![allow(non_camel_case_types)]");
+        self.write_line("#![allow(non_snake_case)]");
+        self.write_line("#![allow(non_upper_case_globals)]");
+        self.write_line("#![allow(trivial_casts)]");
+        self.write_line("#![allow(unsafe_code)]");
+        self.write_line("#![allow(unused_imports)]");
+        self.write_line("#![allow(unused_results)]");
+    }
+
+    pub fn indented<F>(&mut self, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        cb(&mut CodeWriter {
+            writer: self.writer,
+            indent: format!("{}    ", self.indent),
+        });
+    }
+
+    #[allow(dead_code)]
+    pub fn commented<F>(&mut self, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        cb(&mut CodeWriter {
+            writer: self.writer,
+            indent: format!("// {}", self.indent),
+        });
+    }
+
+    pub fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.write_line(first_line);
+        self.indented(cb);
+        self.write_line(last_line);
+    }
+
+    pub fn expr_block<F>(&mut self, prefix: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.block(&format!("{} {{", prefix), "}", cb);
+    }
+
+    pub fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("impl {}", name.as_ref()), cb);
+    }
+
+    pub fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
+    }
+
+    pub fn pub_trait<F>(&mut self, name: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("pub trait {}", name), cb);
+    }
+
+    pub fn field_entry(&mut self, name: &str, value: &str) {
+        self.write_line(&format!("{}: {},", name, value));
+    }
+
+    pub fn field_decl(&mut self, name: &str, field_type: &str) {
+        self.write_line(&format!("{}: {},", name, field_type));
+    }
+
+    pub fn comment(&mut self, comment: &str) {
+        if comment.is_empty() {
+            self.write_line("//");
+        } else {
+            self.write_line(&format!("// {}", comment));
+        }
+    }
+
+    pub fn fn_def(&mut self, sig: &str) {
+        self.write_line(&format!("fn {};", sig));
+    }
+
+    pub fn fn_block<F>(&mut self, public: bool, sig: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        if public {
+            self.expr_block(&format!("pub fn {}", sig), cb);
+        } else {
+            self.expr_block(&format!("fn {}", sig), cb);
+        }
+    }
+
+    pub fn pub_fn<F>(&mut self, sig: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.fn_block(true, sig, cb);
+    }
+}
+
+use super::util::{self, fq_grpc, to_snake_case, MethodType};
+
+struct MethodGen<'a> {
+    proto: &'a MethodDescriptorProto,
+    service_name: String,
+    service_path: String,
+    root_scope: &'a RootScope<'a>,
+}
+
+impl<'a> MethodGen<'a> {
+    fn new(
+        proto: &'a MethodDescriptorProto,
+        service_name: String,
+        service_path: String,
+        root_scope: &'a RootScope<'a>,
+    ) -> MethodGen<'a> {
+        MethodGen {
+            proto,
+            service_name,
+            service_path,
+            root_scope,
+        }
+    }
+
+    fn input(&self) -> String {
+        format!(
+            "super::{}",
+            self.root_scope
+                .find_message(self.proto.get_input_type())
+                .rust_fq_name()
+        )
+    }
+
+    fn output(&self) -> String {
+        format!(
+            "super::{}",
+            self.root_scope
+                .find_message(self.proto.get_output_type())
+                .rust_fq_name()
+        )
+    }
+
+    fn method_type(&self) -> (MethodType, String) {
+        match (
+            self.proto.get_client_streaming(),
+            self.proto.get_server_streaming(),
+        ) {
+            (false, false) => (MethodType::Unary, fq_grpc("MethodType::Unary")),
+            (true, false) => (
+                MethodType::ClientStreaming,
+                fq_grpc("MethodType::ClientStreaming"),
+            ),
+            (false, true) => (
+                MethodType::ServerStreaming,
+                fq_grpc("MethodType::ServerStreaming"),
+            ),
+            (true, true) => (MethodType::Duplex, fq_grpc("MethodType::Duplex")),
+        }
+    }
+
+    fn service_name(&self) -> String {
+        to_snake_case(&self.service_name)
+    }
+
+    fn name(&self) -> String {
+        to_snake_case(self.proto.get_name())
+    }
+
+    fn fq_name(&self) -> String {
+        format!("\"{}/{}\"", self.service_path, &self.proto.get_name())
+    }
+
+    fn const_method_name(&self) -> String {
+        format!(
+            "METHOD_{}_{}",
+            self.service_name().to_uppercase(),
+            self.name().to_uppercase()
+        )
+    }
+
+    fn write_definition(&self, w: &mut CodeWriter) {
+        let head = format!(
+            "const {}: {}<{}, {}> = {} {{",
+            self.const_method_name(),
+            fq_grpc("Method"),
+            self.input(),
+            self.output(),
+            fq_grpc("Method")
+        );
+        let pb_mar = format!(
+            "{} {{ ser: {}, de: {} }}",
+            fq_grpc("Marshaller"),
+            fq_grpc("pb_ser"),
+            fq_grpc("pb_de")
+        );
+        w.block(&head, "};", |w| {
+            w.field_entry("ty", &self.method_type().1);
+            w.field_entry("name", &self.fq_name());
+            w.field_entry("req_mar", &pb_mar);
+            w.field_entry("resp_mar", &pb_mar);
+        });
+    }
+
+    // Method signatures
+    fn unary(&self, method_name: &str) -> String {
+        format!(
+            "{}(&self, req: &{}) -> {}<{}>",
+            method_name,
+            self.input(),
+            fq_grpc("Result"),
+            self.output()
+        )
+    }
+
+    fn unary_opt(&self, method_name: &str) -> String {
+        format!(
+            "{}_opt(&self, req: &{}, opt: {}) -> {}<{}>",
+            method_name,
+            self.input(),
+            fq_grpc("CallOption"),
+            fq_grpc("Result"),
+            self.output()
+        )
+    }
+
+    fn unary_async(&self, method_name: &str) -> String {
+        format!(
+            "{}_async(&self, req: &{}) -> {}<{}<{}>>",
+            method_name,
+            self.input(),
+            fq_grpc("Result"),
+            fq_grpc("ClientUnaryReceiver"),
+            self.output()
+        )
+    }
+
+    fn unary_async_opt(&self, method_name: &str) -> String {
+        format!(
+            "{}_async_opt(&self, req: &{}, opt: {}) -> {}<{}<{}>>",
+            method_name,
+            self.input(),
+            fq_grpc("CallOption"),
+            fq_grpc("Result"),
+            fq_grpc("ClientUnaryReceiver"),
+            self.output()
+        )
+    }
+
+    fn client_streaming(&self, method_name: &str) -> String {
+        format!(
+            "{}(&self) -> {}<({}<{}>, {}<{}>)>",
+            method_name,
+            fq_grpc("Result"),
+            fq_grpc("ClientCStreamSender"),
+            self.input(),
+            fq_grpc("ClientCStreamReceiver"),
+            self.output()
+        )
+    }
+
+    fn client_streaming_opt(&self, method_name: &str) -> String {
+        format!(
+            "{}_opt(&self, opt: {}) -> {}<({}<{}>, {}<{}>)>",
+            method_name,
+            fq_grpc("CallOption"),
+            fq_grpc("Result"),
+            fq_grpc("ClientCStreamSender"),
+            self.input(),
+            fq_grpc("ClientCStreamReceiver"),
+            self.output()
+        )
+    }
+
+    fn server_streaming(&self, method_name: &str) -> String {
+        format!(
+            "{}(&self, req: &{}) -> {}<{}<{}>>",
+            method_name,
+            self.input(),
+            fq_grpc("Result"),
+            fq_grpc("ClientSStreamReceiver"),
+            self.output()
+        )
+    }
+
+    fn server_streaming_opt(&self, method_name: &str) -> String {
+        format!(
+            "{}_opt(&self, req: &{}, opt: {}) -> {}<{}<{}>>",
+            method_name,
+            self.input(),
+            fq_grpc("CallOption"),
+            fq_grpc("Result"),
+            fq_grpc("ClientSStreamReceiver"),
+            self.output()
+        )
+    }
+
+    fn duplex_streaming(&self, method_name: &str) -> String {
+        format!(
+            "{}(&self) -> {}<({}<{}>, {}<{}>)>",
+            method_name,
+            fq_grpc("Result"),
+            fq_grpc("ClientDuplexSender"),
+            self.input(),
+            fq_grpc("ClientDuplexReceiver"),
+            self.output()
+        )
+    }
+
+    fn duplex_streaming_opt(&self, method_name: &str) -> String {
+        format!(
+            "{}_opt(&self, opt: {}) -> {}<({}<{}>, {}<{}>)>",
+            method_name,
+            fq_grpc("CallOption"),
+            fq_grpc("Result"),
+            fq_grpc("ClientDuplexSender"),
+            self.input(),
+            fq_grpc("ClientDuplexReceiver"),
+            self.output()
+        )
+    }
+
+    fn write_client(&self, w: &mut CodeWriter) {
+        let method_name = self.name();
+        match self.method_type().0 {
+            // Unary
+            MethodType::Unary => {
+                w.pub_fn(&self.unary_opt(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.client.unary_call(&{}, req, opt)",
+                        self.const_method_name()
+                    ));
+                });
+                w.write_line("");
+
+                w.pub_fn(&self.unary(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.{}_opt(req, {})",
+                        method_name,
+                        fq_grpc("CallOption::default()")
+                    ));
+                });
+                w.write_line("");
+
+                w.pub_fn(&self.unary_async_opt(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.client.unary_call_async(&{}, req, opt)",
+                        self.const_method_name()
+                    ));
+                });
+                w.write_line("");
+
+                w.pub_fn(&self.unary_async(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.{}_async_opt(req, {})",
+                        method_name,
+                        fq_grpc("CallOption::default()")
+                    ));
+                });
+            }
+
+            // Client streaming
+            MethodType::ClientStreaming => {
+                w.pub_fn(&self.client_streaming_opt(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.client.client_streaming(&{}, opt)",
+                        self.const_method_name()
+                    ));
+                });
+                w.write_line("");
+
+                w.pub_fn(&self.client_streaming(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.{}_opt({})",
+                        method_name,
+                        fq_grpc("CallOption::default()")
+                    ));
+                });
+            }
+
+            // Server streaming
+            MethodType::ServerStreaming => {
+                w.pub_fn(&self.server_streaming_opt(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.client.server_streaming(&{}, req, opt)",
+                        self.const_method_name()
+                    ));
+                });
+                w.write_line("");
+
+                w.pub_fn(&self.server_streaming(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.{}_opt(req, {})",
+                        method_name,
+                        fq_grpc("CallOption::default()")
+                    ));
+                });
+            }
+
+            // Duplex streaming
+            MethodType::Duplex => {
+                w.pub_fn(&self.duplex_streaming_opt(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.client.duplex_streaming(&{}, opt)",
+                        self.const_method_name()
+                    ));
+                });
+                w.write_line("");
+
+                w.pub_fn(&self.duplex_streaming(&method_name), |w| {
+                    w.write_line(&format!(
+                        "self.{}_opt({})",
+                        method_name,
+                        fq_grpc("CallOption::default()")
+                    ));
+                });
+            }
+        };
+    }
+
+    fn write_service(&self, w: &mut CodeWriter) {
+        let req_stream_type = format!("{}<{}>", fq_grpc("RequestStream"), self.input());
+        let (req, req_type, resp_type) = match self.method_type().0 {
+            MethodType::Unary => ("req", self.input(), "UnarySink"),
+            MethodType::ClientStreaming => ("stream", req_stream_type, "ClientStreamingSink"),
+            MethodType::ServerStreaming => ("req", self.input(), "ServerStreamingSink"),
+            MethodType::Duplex => ("stream", req_stream_type, "DuplexSink"),
+        };
+        let sig = format!(
+            "{}(&mut self, ctx: {}, {}: {}, sink: {}<{}>)",
+            self.name(),
+            fq_grpc("RpcContext"),
+            req,
+            req_type,
+            fq_grpc(resp_type),
+            self.output()
+        );
+        w.fn_def(&sig);
+    }
+
+    fn write_bind(&self, w: &mut CodeWriter) {
+        let add = match self.method_type().0 {
+            MethodType::Unary => "add_unary_handler",
+            MethodType::ClientStreaming => "add_client_streaming_handler",
+            MethodType::ServerStreaming => "add_server_streaming_handler",
+            MethodType::Duplex => "add_duplex_streaming_handler",
+        };
+        w.block(
+            &format!(
+                "builder = builder.{}(&{}, move |ctx, req, resp| {{",
+                add,
+                self.const_method_name()
+            ),
+            "});",
+            |w| {
+                w.write_line(&format!("instance.{}(ctx, req, resp)", self.name()));
+            },
+        );
+    }
+}
+
+struct ServiceGen<'a> {
+    proto: &'a ServiceDescriptorProto,
+    methods: Vec<MethodGen<'a>>,
+}
+
+impl<'a> ServiceGen<'a> {
+    fn new(
+        proto: &'a ServiceDescriptorProto,
+        file: &FileDescriptorProto,
+        root_scope: &'a RootScope,
+    ) -> ServiceGen<'a> {
+        let service_path = if file.get_package().is_empty() {
+            format!("/{}", proto.get_name())
+        } else {
+            format!("/{}.{}", file.get_package(), proto.get_name())
+        };
+        let methods = proto
+            .get_method()
+            .iter()
+            .map(|m| {
+                MethodGen::new(
+                    m,
+                    util::to_camel_case(proto.get_name()),
+                    service_path.clone(),
+                    root_scope,
+                )
+            })
+            .collect();
+
+        ServiceGen { proto, methods }
+    }
+
+    fn service_name(&self) -> String {
+        util::to_camel_case(self.proto.get_name())
+    }
+
+    fn client_name(&self) -> String {
+        format!("{}Client", self.service_name())
+    }
+
+    fn write_client(&self, w: &mut CodeWriter) {
+        w.write_line("#[derive(Clone)]");
+        w.pub_struct(&self.client_name(), |w| {
+            w.field_decl("client", "::grpcio::Client");
+        });
+
+        w.write_line("");
+
+        w.impl_self_block(&self.client_name(), |w| {
+            w.pub_fn("new(channel: ::grpcio::Channel) -> Self", |w| {
+                w.expr_block(&self.client_name(), |w| {
+                    w.field_entry("client", "::grpcio::Client::new(channel)");
+                });
+            });
+
+            for method in &self.methods {
+                w.write_line("");
+                method.write_client(w);
+            }
+            w.pub_fn(
+                "spawn<F>(&self, f: F) where F: ::futures::Future<Output = ()> + Send + 'static",
+                |w| {
+                    w.write_line("self.client.spawn(f)");
+                },
+            )
+        });
+    }
+
+    fn write_server(&self, w: &mut CodeWriter) {
+        w.pub_trait(&self.service_name(), |w| {
+            for method in &self.methods {
+                method.write_service(w);
+            }
+        });
+
+        w.write_line("");
+
+        let s = format!(
+            "create_{}<S: {} + Send + Clone + 'static>(s: S) -> {}",
+            to_snake_case(&self.service_name()),
+            self.service_name(),
+            fq_grpc("Service")
+        );
+        w.pub_fn(&s, |w| {
+            w.write_line("let mut builder = ::grpcio::ServiceBuilder::new();");
+            for method in &self.methods[0..self.methods.len() - 1] {
+                w.write_line("let mut instance = s.clone();");
+                method.write_bind(w);
+            }
+
+            w.write_line("let mut instance = s;");
+            self.methods[self.methods.len() - 1].write_bind(w);
+
+            w.write_line("builder.build()");
+        });
+    }
+
+    fn write_method_definitions(&self, w: &mut CodeWriter) {
+        for (i, method) in self.methods.iter().enumerate() {
+            if i != 0 {
+                w.write_line("");
+            }
+
+            method.write_definition(w);
+        }
+    }
+
+    fn write(&self, w: &mut CodeWriter) {
+        self.write_method_definitions(w);
+        w.write_line("");
+        self.write_client(w);
+        w.write_line("");
+        self.write_server(w);
+    }
+}
+
+fn gen_file(
+    file: &FileDescriptorProto,
+    root_scope: &RootScope,
+) -> Option<compiler_plugin::GenResult> {
+    if file.get_service().is_empty() {
+        return None;
+    }
+
+    let base = protobuf::descriptorx::proto_path_to_rust_mod(file.get_name());
+
+    let mut v = Vec::new();
+    {
+        let mut w = CodeWriter::new(&mut v);
+        w.write_generated();
+
+        for service in file.get_service() {
+            w.write_line("");
+            ServiceGen::new(service, file, root_scope).write(&mut w);
+        }
+    }
+
+    Some(compiler_plugin::GenResult {
+        name: base + "_grpc.rs",
+        content: v,
+    })
+}
+
+pub fn gen(
+    file_descriptors: &[FileDescriptorProto],
+    files_to_generate: &[String],
+) -> Vec<compiler_plugin::GenResult> {
+    let files_map: HashMap<&str, &FileDescriptorProto> =
+        file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
+
+    let root_scope = RootScope { file_descriptors };
+
+    let mut results = Vec::new();
+
+    for file_name in files_to_generate {
+        let file = files_map[&file_name[..]];
+
+        if file.get_service().is_empty() {
+            continue;
+        }
+
+        results.extend(gen_file(file, &root_scope).into_iter());
+    }
+
+    results
+}
+
+pub fn protoc_gen_grpc_rust_main() {
+    compiler_plugin::plugin_main(gen);
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..3c7052b
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,8 @@
+// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
+
+#[cfg(feature = "protobuf-codec")]
+pub mod codegen;
+#[cfg(feature = "prost-codec")]
+pub mod prost_codegen;
+
+mod util;
diff --git a/src/prost_codegen.rs b/src/prost_codegen.rs
new file mode 100644
index 0000000..e2d7533
--- /dev/null
+++ b/src/prost_codegen.rs
@@ -0,0 +1,538 @@
+// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
+
+use std::io::{Error, ErrorKind, Read};
+use std::path::Path;
+use std::{fs, io, process::Command};
+
+use derive_new::new;
+use prost::Message;
+use prost_build::{protoc, protoc_include, Config, Method, Service, ServiceGenerator};
+use prost_types::FileDescriptorSet;
+
+use crate::util::{fq_grpc, to_snake_case, MethodType};
+
+/// Returns the names of all packages compiled.
+pub fn compile_protos<P>(protos: &[P], includes: &[P], out_dir: &str) -> io::Result<Vec<String>>
+where
+    P: AsRef<Path>,
+{
+    let mut prost_config = Config::new();
+    prost_config.service_generator(Box::new(Generator));
+    prost_config.out_dir(out_dir);
+
+    // Create a file descriptor set for the protocol files.
+    let tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?;
+    let descriptor_set = tmp.path().join("prost-descriptor-set");
+
+    let mut cmd = Command::new(protoc());
+    cmd.arg("--include_imports")
+        .arg("--include_source_info")
+        .arg("-o")
+        .arg(&descriptor_set);
+
+    for include in includes {
+        cmd.arg("-I").arg(include.as_ref());
+    }
+
+    // Set the protoc include after the user includes in case the user wants to
+    // override one of the built-in .protos.
+    cmd.arg("-I").arg(protoc_include());
+
+    for proto in protos {
+        cmd.arg(proto.as_ref());
+    }
+
+    let output = cmd.output()?;
+    if !output.status.success() {
+        return Err(Error::new(
+            ErrorKind::Other,
+            format!("protoc failed: {}", String::from_utf8_lossy(&output.stderr)),
+        ));
+    }
+
+    let mut buf = Vec::new();
+    fs::File::open(descriptor_set)?.read_to_end(&mut buf)?;
+    let descriptor_set = FileDescriptorSet::decode(buf.as_slice())?;
+
+    // Get the package names from the descriptor set.
+    let mut packages: Vec<_> = descriptor_set
+        .file
+        .iter()
+        .filter_map(|f| f.package.clone())
+        .collect();
+    packages.sort();
+    packages.dedup();
+
+    // FIXME(https://github.com/danburkert/prost/pull/155)
+    // Unfortunately we have to forget the above work and use `compile_protos` to
+    // actually generate the Rust code.
+    prost_config.compile_protos(protos, includes)?;
+
+    Ok(packages)
+}
+
+struct Generator;
+
+impl ServiceGenerator for Generator {
+    fn generate(&mut self, service: Service, buf: &mut String) {
+        generate_methods(&service, buf);
+        generate_client(&service, buf);
+        generate_server(&service, buf);
+    }
+}
+
+fn generate_methods(service: &Service, buf: &mut String) {
+    let service_path = if service.package.is_empty() {
+        format!("/{}", service.proto_name)
+    } else {
+        format!("/{}.{}", service.package, service.proto_name)
+    };
+
+    for method in &service.methods {
+        generate_method(&service.name, &service_path, method, buf);
+    }
+}
+
+fn const_method_name(service_name: &str, method: &Method) -> String {
+    format!(
+        "METHOD_{}_{}",
+        to_snake_case(service_name).to_uppercase(),
+        method.name.to_uppercase()
+    )
+}
+
+fn generate_method(service_name: &str, service_path: &str, method: &Method, buf: &mut String) {
+    let name = const_method_name(service_name, method);
+    let ty = format!(
+        "{}<{}, {}>",
+        fq_grpc("Method"),
+        method.input_type,
+        method.output_type
+    );
+
+    buf.push_str("const ");
+    buf.push_str(&name);
+    buf.push_str(": ");
+    buf.push_str(&ty);
+    buf.push_str(" = ");
+    generate_method_body(service_path, method, buf);
+}
+
+fn generate_method_body(service_path: &str, method: &Method, buf: &mut String) {
+    let ty = fq_grpc(&MethodType::from_method(method).to_string());
+    let pr_mar = format!(
+        "{} {{ ser: {}, de: {} }}",
+        fq_grpc("Marshaller"),
+        fq_grpc("pr_ser"),
+        fq_grpc("pr_de")
+    );
+
+    buf.push_str(&fq_grpc("Method"));
+    buf.push('{');
+    generate_field_init("ty", &ty, buf);
+    generate_field_init(
+        "name",
+        &format!("\"{}/{}\"", service_path, method.proto_name),
+        buf,
+    );
+    generate_field_init("req_mar", &pr_mar, buf);
+    generate_field_init("resp_mar", &pr_mar, buf);
+    buf.push_str("};\n");
+}
+
+// TODO share this code with protobuf codegen
+impl MethodType {
+    fn from_method(method: &Method) -> MethodType {
+        match (method.client_streaming, method.server_streaming) {
+            (false, false) => MethodType::Unary,
+            (true, false) => MethodType::ClientStreaming,
+            (false, true) => MethodType::ServerStreaming,
+            (true, true) => MethodType::Duplex,
+        }
+    }
+}
+
+fn generate_field_init(name: &str, value: &str, buf: &mut String) {
+    buf.push_str(name);
+    buf.push_str(": ");
+    buf.push_str(value);
+    buf.push_str(", ");
+}
+
+fn generate_client(service: &Service, buf: &mut String) {
+    let client_name = format!("{}Client", service.name);
+    buf.push_str("#[derive(Clone)]\n");
+    buf.push_str("pub struct ");
+    buf.push_str(&client_name);
+    buf.push_str(" { client: ::grpcio::Client }\n");
+
+    buf.push_str("impl ");
+    buf.push_str(&client_name);
+    buf.push_str(" {\n");
+    generate_ctor(&client_name, buf);
+    generate_client_methods(service, buf);
+    generate_spawn(buf);
+    buf.push_str("}\n")
+}
+
+fn generate_ctor(client_name: &str, buf: &mut String) {
+    buf.push_str("pub fn new(channel: ::grpcio::Channel) -> Self { ");
+    buf.push_str(client_name);
+    buf.push_str(" { client: ::grpcio::Client::new(channel) }");
+    buf.push_str("}\n");
+}
+
+fn generate_client_methods(service: &Service, buf: &mut String) {
+    for method in &service.methods {
+        generate_client_method(&service.name, method, buf);
+    }
+}
+
+fn generate_client_method(service_name: &str, method: &Method, buf: &mut String) {
+    let name = &format!(
+        "METHOD_{}_{}",
+        to_snake_case(service_name).to_uppercase(),
+        method.name.to_uppercase()
+    );
+    match MethodType::from_method(method) {
+        MethodType::Unary => {
+            ClientMethod::new(
+                &method.name,
+                true,
+                Some(&method.input_type),
+                false,
+                vec![&method.output_type],
+                "unary_call",
+                name,
+            )
+            .generate(buf);
+            ClientMethod::new(
+                &method.name,
+                false,
+                Some(&method.input_type),
+                false,
+                vec![&method.output_type],
+                "unary_call",
+                name,
+            )
+            .generate(buf);
+            ClientMethod::new(
+                &method.name,
+                true,
+                Some(&method.input_type),
+                true,
+                vec![&format!(
+                    "{}<{}>",
+                    fq_grpc("ClientUnaryReceiver"),
+                    method.output_type
+                )],
+                "unary_call",
+                name,
+            )
+            .generate(buf);
+            ClientMethod::new(
+                &method.name,
+                false,
+                Some(&method.input_type),
+                true,
+                vec![&format!(
+                    "{}<{}>",
+                    fq_grpc("ClientUnaryReceiver"),
+                    method.output_type
+                )],
+                "unary_call",
+                name,
+            )
+            .generate(buf);
+        }
+        MethodType::ClientStreaming => {
+            ClientMethod::new(
+                &method.name,
+                true,
+                None,
+                false,
+                vec![
+                    &format!("{}<{}>", fq_grpc("ClientCStreamSender"), method.input_type),
+                    &format!(
+                        "{}<{}>",
+                        fq_grpc("ClientCStreamReceiver"),
+                        method.output_type
+                    ),
+                ],
+                "client_streaming",
+                name,
+            )
+            .generate(buf);
+            ClientMethod::new(
+                &method.name,
+                false,
+                None,
+                false,
+                vec![
+                    &format!("{}<{}>", fq_grpc("ClientCStreamSender"), method.input_type),
+                    &format!(
+                        "{}<{}>",
+                        fq_grpc("ClientCStreamReceiver"),
+                        method.output_type
+                    ),
+                ],
+                "client_streaming",
+                name,
+            )
+            .generate(buf);
+        }
+        MethodType::ServerStreaming => {
+            ClientMethod::new(
+                &method.name,
+                true,
+                Some(&method.input_type),
+                false,
+                vec![&format!(
+                    "{}<{}>",
+                    fq_grpc("ClientSStreamReceiver"),
+                    method.output_type
+                )],
+                "server_streaming",
+                name,
+            )
+            .generate(buf);
+            ClientMethod::new(
+                &method.name,
+                false,
+                Some(&method.input_type),
+                false,
+                vec![&format!(
+                    "{}<{}>",
+                    fq_grpc("ClientSStreamReceiver"),
+                    method.output_type
+                )],
+                "server_streaming",
+                name,
+            )
+            .generate(buf);
+        }
+        MethodType::Duplex => {
+            ClientMethod::new(
+                &method.name,
+                true,
+                None,
+                false,
+                vec![
+                    &format!("{}<{}>", fq_grpc("ClientDuplexSender"), method.input_type),
+                    &format!(
+                        "{}<{}>",
+                        fq_grpc("ClientDuplexReceiver"),
+                        method.output_type
+                    ),
+                ],
+                "duplex_streaming",
+                name,
+            )
+            .generate(buf);
+            ClientMethod::new(
+                &method.name,
+                false,
+                None,
+                false,
+                vec![
+                    &format!("{}<{}>", fq_grpc("ClientDuplexSender"), method.input_type),
+                    &format!(
+                        "{}<{}>",
+                        fq_grpc("ClientDuplexReceiver"),
+                        method.output_type
+                    ),
+                ],
+                "duplex_streaming",
+                name,
+            )
+            .generate(buf);
+        }
+    }
+}
+
+#[derive(new)]
+struct ClientMethod<'a> {
+    method_name: &'a str,
+    opt: bool,
+    request: Option<&'a str>,
+    r#async: bool,
+    result_types: Vec<&'a str>,
+    inner_method_name: &'a str,
+    data_name: &'a str,
+}
+
+impl<'a> ClientMethod<'a> {
+    fn generate(&self, buf: &mut String) {
+        buf.push_str("pub fn ");
+
+        buf.push_str(self.method_name);
+        if self.r#async {
+            buf.push_str("_async");
+        }
+        if self.opt {
+            buf.push_str("_opt");
+        }
+
+        buf.push_str("(&self");
+        if let Some(req) = self.request {
+            buf.push_str(", req: &");
+            buf.push_str(req);
+        }
+        if self.opt {
+            buf.push_str(", opt: ");
+            buf.push_str(&fq_grpc("CallOption"));
+        }
+        buf.push_str(") -> ");
+
+        buf.push_str(&fq_grpc("Result"));
+        buf.push('<');
+        if self.result_types.len() != 1 {
+            buf.push('(');
+        }
+        for rt in &self.result_types {
+            buf.push_str(rt);
+            buf.push(',');
+        }
+        if self.result_types.len() != 1 {
+            buf.push(')');
+        }
+        buf.push_str("> { ");
+        if self.opt {
+            self.generate_inner_body(buf);
+        } else {
+            self.generate_opt_body(buf);
+        }
+        buf.push_str(" }\n");
+    }
+
+    // Method delegates to the `_opt` version of the method.
+    fn generate_opt_body(&self, buf: &mut String) {
+        buf.push_str("self.");
+        buf.push_str(self.method_name);
+        if self.r#async {
+            buf.push_str("_async");
+        }
+        buf.push_str("_opt(");
+        if self.request.is_some() {
+            buf.push_str("req, ");
+        }
+        buf.push_str(&fq_grpc("CallOption::default()"));
+        buf.push(')');
+    }
+
+    // Method delegates to the inner client.
+    fn generate_inner_body(&self, buf: &mut String) {
+        buf.push_str("self.client.");
+        buf.push_str(self.inner_method_name);
+        if self.r#async {
+            buf.push_str("_async");
+        }
+        buf.push_str("(&");
+        buf.push_str(self.data_name);
+        if self.request.is_some() {
+            buf.push_str(", req");
+        }
+        buf.push_str(", opt)");
+    }
+}
+
+fn generate_spawn(buf: &mut String) {
+    buf.push_str(
+        "pub fn spawn<F>(&self, f: F) \
+         where F: ::futures::Future<Output = ()> + Send + 'static {\
+         self.client.spawn(f)\
+         }\n",
+    );
+}
+
+fn generate_server(service: &Service, buf: &mut String) {
+    buf.push_str("pub trait ");
+    buf.push_str(&service.name);
+    buf.push_str(" {\n");
+    generate_server_methods(service, buf);
+    buf.push_str("}\n");
+
+    buf.push_str("pub fn create_");
+    buf.push_str(&to_snake_case(&service.name));
+    buf.push_str("<S: ");
+    buf.push_str(&service.name);
+    buf.push_str(" + Send + Clone + 'static>(s: S) -> ");
+    buf.push_str(&fq_grpc("Service"));
+    buf.push_str(" {\n");
+    buf.push_str("let mut builder = ::grpcio::ServiceBuilder::new();\n");
+
+    for method in &service.methods[0..service.methods.len() - 1] {
+        buf.push_str("let mut instance = s.clone();\n");
+        generate_method_bind(&service.name, method, buf);
+    }
+
+    buf.push_str("let mut instance = s;\n");
+    generate_method_bind(
+        &service.name,
+        &service.methods[service.methods.len() - 1],
+        buf,
+    );
+
+    buf.push_str("builder.build()\n");
+    buf.push_str("}\n");
+}
+
+fn generate_server_methods(service: &Service, buf: &mut String) {
+    for method in &service.methods {
+        let method_type = MethodType::from_method(method);
+        let request_arg = match method_type {
+            MethodType::Unary | MethodType::ServerStreaming => {
+                format!("req: {}", method.input_type)
+            }
+            MethodType::ClientStreaming | MethodType::Duplex => format!(
+                "stream: {}<{}>",
+                fq_grpc("RequestStream"),
+                method.input_type
+            ),
+        };
+        let response_type = match method_type {
+            MethodType::Unary => "UnarySink",
+            MethodType::ClientStreaming => "ClientStreamingSink",
+            MethodType::ServerStreaming => "ServerStreamingSink",
+            MethodType::Duplex => "DuplexSink",
+        };
+        generate_server_method(method, &request_arg, response_type, buf);
+    }
+}
+
+fn generate_server_method(
+    method: &Method,
+    request_arg: &str,
+    response_type: &str,
+    buf: &mut String,
+) {
+    buf.push_str("fn ");
+    buf.push_str(&method.name);
+    buf.push_str("(&mut self, ctx: ");
+    buf.push_str(&fq_grpc("RpcContext"));
+    buf.push_str(", ");
+    buf.push_str(request_arg);
+    buf.push_str(", sink: ");
+    buf.push_str(&fq_grpc(response_type));
+    buf.push('<');
+    buf.push_str(&method.output_type);
+    buf.push('>');
+    buf.push_str(");\n");
+}
+
+fn generate_method_bind(service_name: &str, method: &Method, buf: &mut String) {
+    let add_name = match MethodType::from_method(method) {
+        MethodType::Unary => "add_unary_handler",
+        MethodType::ClientStreaming => "add_client_streaming_handler",
+        MethodType::ServerStreaming => "add_server_streaming_handler",
+        MethodType::Duplex => "add_duplex_streaming_handler",
+    };
+
+    buf.push_str("builder = builder.");
+    buf.push_str(add_name);
+    buf.push_str("(&");
+    buf.push_str(&const_method_name(service_name, method));
+    buf.push_str(", move |ctx, req, resp| instance.");
+    buf.push_str(&method.name);
+    buf.push_str("(ctx, req, resp));\n");
+}
diff --git a/src/util.rs b/src/util.rs
new file mode 100644
index 0000000..c46be2d
--- /dev/null
+++ b/src/util.rs
@@ -0,0 +1,155 @@
+// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
+
+use std::fmt;
+use std::str;
+
+// A struct that divide a name into serveral parts that meets rust's guidelines.
+struct NameSpliter<'a> {
+    name: &'a [u8],
+    pos: usize,
+}
+
+impl<'a> NameSpliter<'a> {
+    fn new(s: &str) -> NameSpliter {
+        NameSpliter {
+            name: s.as_bytes(),
+            pos: 0,
+        }
+    }
+}
+
+impl<'a> Iterator for NameSpliter<'a> {
+    type Item = &'a str;
+
+    fn next(&mut self) -> Option<&'a str> {
+        if self.pos == self.name.len() {
+            return None;
+        }
+        // skip all prefix '_'
+        while self.pos < self.name.len() && self.name[self.pos] == b'_' {
+            self.pos += 1;
+        }
+        let mut pos = self.name.len();
+        let mut upper_len = 0;
+        let mut meet_lower = false;
+        for i in self.pos..self.name.len() {
+            let c = self.name[i];
+            if b'A' <= c && c <= b'Z' {
+                if meet_lower {
+                    // So it should be AaA or aaA
+                    pos = i;
+                    break;
+                }
+                upper_len += 1;
+            } else if c == b'_' {
+                pos = i;
+                break;
+            } else {
+                meet_lower = true;
+                if upper_len > 1 {
+                    // So it should be AAa
+                    pos = i - 1;
+                    break;
+                }
+            }
+        }
+        let s = str::from_utf8(&self.name[self.pos..pos]).unwrap();
+        self.pos = pos;
+        Some(s)
+    }
+}
+
+/// Adjust method name to follow rust-guidelines.
+pub fn to_snake_case(name: &str) -> String {
+    let mut snake_method_name = String::with_capacity(name.len());
+    for s in NameSpliter::new(name) {
+        snake_method_name.push_str(&s.to_lowercase());
+        snake_method_name.push('_');
+    }
+    snake_method_name.pop();
+    snake_method_name
+}
+
+#[cfg(feature = "protobuf-codec")]
+pub fn to_camel_case(name: &str) -> String {
+    let mut camel_case_name = String::with_capacity(name.len());
+    for s in NameSpliter::new(name) {
+        let mut chs = s.chars();
+        camel_case_name.extend(chs.next().unwrap().to_uppercase());
+        camel_case_name.push_str(&s[1..].to_lowercase());
+    }
+    camel_case_name
+}
+
+pub fn fq_grpc(item: &str) -> String {
+    format!("::grpcio::{}", item)
+}
+
+pub enum MethodType {
+    Unary,
+    ClientStreaming,
+    ServerStreaming,
+    Duplex,
+}
+
+impl fmt::Display for MethodType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "{}",
+            match self {
+                MethodType::Unary => "MethodType::Unary",
+                MethodType::ClientStreaming => "MethodType::ClientStreaming",
+                MethodType::ServerStreaming => "MethodType::ServerStreaming",
+                MethodType::Duplex => "MethodType::Duplex",
+            }
+        )
+    }
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn test_snake_name() {
+        let cases = vec![
+            ("AsyncRequest", "async_request"),
+            ("asyncRequest", "async_request"),
+            ("async_request", "async_request"),
+            ("createID", "create_id"),
+            ("AsyncRClient", "async_r_client"),
+            ("CreateIDForReq", "create_id_for_req"),
+            ("Create_ID_For_Req", "create_id_for_req"),
+            ("Create_ID_For__Req", "create_id_for_req"),
+            ("ID", "id"),
+            ("id", "id"),
+        ];
+
+        for (origin, exp) in cases {
+            let res = super::to_snake_case(&origin);
+            assert_eq!(res, exp);
+        }
+    }
+
+    #[test]
+    #[cfg(feature = "protobuf-codec")]
+    fn test_camel_name() {
+        let cases = vec![
+            ("AsyncRequest", "AsyncRequest"),
+            ("asyncRequest", "AsyncRequest"),
+            ("async_request", "AsyncRequest"),
+            ("createID", "CreateId"),
+            ("AsyncRClient", "AsyncRClient"),
+            ("async_r_client", "AsyncRClient"),
+            ("CreateIDForReq", "CreateIdForReq"),
+            ("Create_ID_For_Req", "CreateIdForReq"),
+            ("Create_ID_For__Req", "CreateIdForReq"),
+            ("ID", "Id"),
+            ("id", "Id"),
+        ];
+
+        for (origin, exp) in cases {
+            let res = super::to_camel_case(&origin);
+            assert_eq!(res, exp);
+        }
+    }
+}