rustgrpcrust-tonicprosttonic-build

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


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
    |

Solution

  • 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.