diff --git a/Cargo.toml b/Cargo.toml index 42c7f16de..478564923 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,6 @@ members = [ "tests/included_service", "tests/same_name", "tests/wellknown", + "tests/extern_path/uuid", + "tests/extern_path/my_application" ] diff --git a/tests/extern_path/my_application/Cargo.toml b/tests/extern_path/my_application/Cargo.toml new file mode 100644 index 000000000..28564b5c8 --- /dev/null +++ b/tests/extern_path/my_application/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "my_application" +version = "0.1.0" +authors = ["Danny Hua "] +edition = "2018" +publish = false +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tonic = { path= "../../../tonic" } +prost = "0.6" +prost-types = "0.6" +uuid = { path= "../uuid" } + +[build-dependencies] +tonic-build = { path= "../../../tonic-build" } diff --git a/tests/extern_path/my_application/build.rs b/tests/extern_path/my_application/build.rs new file mode 100644 index 000000000..5d25b018d --- /dev/null +++ b/tests/extern_path/my_application/build.rs @@ -0,0 +1,11 @@ +fn main() -> Result<(), std::io::Error> { + tonic_build::configure() + .build_server(false) + .build_client(true) + .extern_path(".uuid", "::uuid") + .compile( + &["service.proto", "uuid.proto"], + &["../proto/my_application", "../proto/uuid"], + )?; + Ok(()) +} diff --git a/tests/extern_path/my_application/src/main.rs b/tests/extern_path/my_application/src/main.rs new file mode 100644 index 000000000..2919ed4af --- /dev/null +++ b/tests/extern_path/my_application/src/main.rs @@ -0,0 +1,26 @@ +use uuid::DoSomething; +mod pb { + tonic::include_proto!("my_application"); +} +fn main() { + // verify that extern_path to replace proto's with impl's from other crates works. + let message = pb::MyMessage { + message_id: Some(::uuid::Uuid { + uuid_str: "".to_string(), + }), + some_payload: "".to_string(), + }; + dbg!(message.message_id.unwrap().do_it()); +} +#[cfg(test)] +#[test] +fn service_types_have_extern_types() { + // verify that extern_path to replace proto's with impl's from other crates works. + let message = pb::MyMessage { + message_id: Some(::uuid::Uuid { + uuid_str: "not really a uuid".to_string(), + }), + some_payload: "payload".to_string(), + }; + assert_eq!(message.message_id.unwrap().do_it(), "Done"); +} diff --git a/tests/extern_path/proto/my_application/service.proto b/tests/extern_path/proto/my_application/service.proto new file mode 100644 index 000000000..25163ef32 --- /dev/null +++ b/tests/extern_path/proto/my_application/service.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package my_application; + +option go_package = "my_applicationpb"; +option java_multiple_files = true; +option java_outer_classname = "ServiceProto"; +option java_package = "com.my_application"; + + +import "uuid.proto"; + +message MyMessage { + uuid.Uuid message_id = 1; + string some_payload = 2; +} + +service MyService { + rpc GetUuid(MyMessage) returns (uuid.Uuid){ + + } + rpc GetMyMessage(uuid.Uuid) returns (MyMessage){ + + } +} diff --git a/tests/extern_path/proto/uuid/uuid.proto b/tests/extern_path/proto/uuid/uuid.proto new file mode 100644 index 000000000..9255ed6c0 --- /dev/null +++ b/tests/extern_path/proto/uuid/uuid.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package uuid; + +option go_package = "uuidpb"; +option java_multiple_files = true; +option java_outer_classname = "UuidProto"; +option java_package = "com.uuid"; + + +message Uuid { + string uuid_str = 1; +} diff --git a/tests/extern_path/uuid/Cargo.toml b/tests/extern_path/uuid/Cargo.toml new file mode 100644 index 000000000..00061c77e --- /dev/null +++ b/tests/extern_path/uuid/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "uuid" +version = "0.1.0" +authors = ["Danny Hua "] +edition = "2018" +publish = false +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +prost = "0.6" +bytes = "0.5" +[build-dependencies] +prost-build = "0.6" diff --git a/tests/extern_path/uuid/build.rs b/tests/extern_path/uuid/build.rs new file mode 100644 index 000000000..9b2b01d26 --- /dev/null +++ b/tests/extern_path/uuid/build.rs @@ -0,0 +1,3 @@ +fn main() { + prost_build::compile_protos(&["uuid/uuid.proto"], &["../proto/"]).unwrap(); +} diff --git a/tests/extern_path/uuid/src/lib.rs b/tests/extern_path/uuid/src/lib.rs new file mode 100644 index 000000000..4245ec21a --- /dev/null +++ b/tests/extern_path/uuid/src/lib.rs @@ -0,0 +1,11 @@ +include!(concat!(env!("OUT_DIR"), "/uuid.rs")); + +pub trait DoSomething { + fn do_it(&self) -> String; +} + +impl DoSomething for Uuid { + fn do_it(&self) -> String { + "Done".to_string() + } +} diff --git a/tonic-build/src/lib.rs b/tonic-build/src/lib.rs index 8b1ec889a..0c75ee224 100644 --- a/tonic-build/src/lib.rs +++ b/tonic-build/src/lib.rs @@ -114,6 +114,8 @@ impl Builder { /// Declare externally provided Protobuf package or type. /// /// Passed directly to `prost_build::Config.extern_path`. + /// Note that both the Protobuf path and the rust package paths should both be fully qualified. + /// i.e. Protobuf paths should start with "." and rust paths should start with "::" pub fn extern_path(mut self, proto_path: impl AsRef, rust_path: impl AsRef) -> Self { self.extern_path.push(( proto_path.as_ref().to_string(), @@ -318,7 +320,9 @@ fn generate_doc_comments>(comments: &[T]) -> TokenStream { } fn replace_wellknown(proto_path: &str, method: &Method) -> (TokenStream, TokenStream) { - let request = if method.input_proto_type.starts_with(".google.protobuf") { + let request = if method.input_proto_type.starts_with(".google.protobuf") + || method.input_type.starts_with("::") + { method.input_type.parse::().unwrap() } else { syn::parse_str::(&format!("{}::{}", proto_path, method.input_type)) @@ -326,7 +330,9 @@ fn replace_wellknown(proto_path: &str, method: &Method) -> (TokenStream, TokenSt .to_token_stream() }; - let response = if method.output_proto_type.starts_with(".google.protobuf") { + let response = if method.output_proto_type.starts_with(".google.protobuf") + || method.output_type.starts_with("::") + { method.output_type.parse::().unwrap() } else { syn::parse_str::(&format!("{}::{}", proto_path, method.output_type))