Error using package name as "proto.messages.v1" in .proto with prost, and tonic in rust

538 Views Asked by At

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
    |
1

There are 1 best solutions below

0
On BEST ANSWER

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. The proto_path is defined to be proto and then individual protobuf files must include one of the proto_path's (we only have proto) and the package 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
Update

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 revised package 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 { ... }} and use updated.