I'm new to Rust and trying to create a simple gRPC application. Here is the directory structure:
grpc-protobuf
├── Cargo.toml
├── build.rs
├── proto
│ ├── hello
│ │ └── hello.proto
│ └── messages
│ └── hello.proto
└── src
└── lib.rs
I have the protobuf messages defined under proto/messages/hello.proto and below is the file content:
syntax = "proto3";
package proto.messages.v1;
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
The service that uses the messages is defined as proto/hello/hello.proto:
syntax = "proto3";
package hello;
import "messages/hello.proto";
service Greeter {
rpc SayHello (proto.messages.v1.HelloRequest) returns (proto.messages.v1.HelloResponse) {}
}
And here is how I'm creating a package:
pub mod hello {
tonic::include_proto!("hello");
}
pub mod messages {
tonic::include_proto!("proto.messages.v1");
}
As I try to build the package I get the following errors:
➜ cargo build -p grpc-protobuf
Compiling grpc-protobuf v0.1.0 (/Users/gaurav.gahlot/workspace/rusty/rust-grpc/grpc-protobuf)
error[E0433]: failed to resolve: could not find `proto` in the crate root
--> /Users/gaurav.gahlot/workspace/rusty/rust-grpc/target/debug/build/grpc-protobuf-6fc85551b5cb3a84/out/hello.rs:91:31
|
91 | super::super::proto::messages::v1::HelloRequest,
| ^^^^^ could not find `proto` in the crate root
error[E0433]: failed to resolve: could not find `proto` in the crate root
--> /Users/gaurav.gahlot/workspace/rusty/rust-grpc/target/debug/build/grpc-protobuf-6fc85551b5cb3a84/out/hello.rs:94:43
|
94 | tonic::Response<super::super::proto::messages::v1::HelloResponse>,
| ^^^^^ could not find `proto` in the crate root
error[E0433]: failed to resolve: could not find `proto` in the crate root
--> /Users/gaurav.gahlot/workspace/rusty/rust-grpc/target/debug/build/grpc-protobuf-6fc85551b5cb3a84/out/hello.rs:124:51
|
I've replicated your file structure (I'm using bin
rather than lib
):
.
├── build.rs
├── Cargo.toml
├── proto
│ ├── hello
│ └── messages
├── src
│ └── main.rs
└── target
proto/hello/hello.proto
:
syntax = "proto3";
package hello;
import "messages/messages.proto";
service Greeter {
rpc SayHello (messages.HelloRequest) returns (messages.HelloResponse) {}
}
proto/messages/messages.proto
:
syntax = "proto3";
package messages;
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
build.rs
:
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.build_server(true)
.compile(
&[
"proto/hello/hello.proto",
"proto/messages/messages.proto",
],
&["proto"],
)?;
Ok(())
}
NOTE
tonic-build
permits crate relative addresses. Theproto_path
is defined to beproto
and then individual protobuf files must include one of theproto_path
's (we only haveproto
) and thepackage
path.
main.rs
:
pub mod hello {
tonic::include_proto!("hello");
}
pub mod messages {
tonic::include_proto!("messages");
}
use hello::{
greeter_server::{Greeter, GreeterServer},
};
use messages::{
HelloRequest, HelloResponse,
};
Coming from using gRPC mostly with Go and some Python, I like how tonic
works but found the protoc
generation initially confusing. tonic-build
uses OUT_DIR
as the location for the protoc
generated sources. You can:
ls target/debug/build/grpc-protobuf*/out
Yielding:
target/debug/build/grpc-protobuf-be913437690683ab/out:
hello.rs messages.rs
When you have a nested package
hierarchy, e.g. package messages.v1
, there are several changes in particular to the rust mod
hierarchy to reflect the package
hierarchy:
.
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── proto
│ ├── hello
│ │ └── hello.proto
│ └── messages
│ └── v1
│ └── messages.proto
├── README.md
├── src
│ └── main.rs
└── target
NOTE folder structure
proto/messages/v1
to reflect revisedpackage
name.
proto/hello/hello.proto
:
syntax = "proto3";
package hello;
import "messages/v1/messages.proto";
service Greeter {
rpc SayHello (messages.v1.HelloRequest)
returns (messages.v1.HelloResponse) {}
}
NOTE
messages.v1.Hello*
proto/messages/v1/messages.proto
:
syntax = "proto3";
package messages.v1;
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
NOTE
package
path updated.
build.rs
:
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.build_server(true)
.compile(
&[
"proto/hello/hello.proto",
"proto/messages/v1/messages.proto",
],
&["proto"],
)?;
Ok(())
}
NOTE proto imports updated.
main.rs
:
pub mod hello {
tonic::include_proto!("hello");
}
pub mod messages {
pub mod v1 {
tonic::include_proto!("messages.v1");
}
}
use hello::{
greeter_server::{Greeter,GreeterServer},
};
use messages::v1::{
HelloRequest, HelloResponse,
};
NOTE
pub mod messages { pub mod v1 { ... }}
anduse
updated.