website/content/blog/prost-import-external-schemas.md
2024-10-06 12:51:37 -04:00

3 KiB

title date draft tags math medium_enabled
Importing External Schemas in Prost 2024-10-05T11:19:37-07:00 false
false false

Prost is a popular Protocol Buffers implementation written in Rust. The protoc command is commonly used to generate serializers and de-serializers for many different languages. However, the Prost team instead recommends compiling the Protocol Buffers schema directly within build.rs.

In this post, I'll give a quick tutorial on how I compiled my Protocol Buffers schema when I needed to depend on another package with its own PB messages.

Let's say I have a Rust package called people. It contains a file called Person.proto with the following contents:

syntax = "proto3";
package people;

message Employee {
  string name = 1;
  string position = 2;
}

For sake of our example, we'll assume they exposed the data structures via a proto submodule. However, you can check what the module name actually is by looking at the package's codebase for something like

pub mod namespace_name_goes_here {
    #![allow(missing_docs)]
    include!(concat!(env!("OUT_DIR"), "/people.rs"));
}

Now to the package we're creating (which I'll call company), First, make sure that you added the people package to the Cargo.toml.

Then say that we have the following file schemas/Team.proto

syntax = "proto3";
package company;

import "Person.proto"

message Team {
  repeated people.Employee members = 1;
}

To compile our new message, we need to tell build.rs where it can find both the Person.proto and the Team.proto schema files, as well as how to reference the messages in the people namespace.

fn main() {
    // Other awesome build stuff
    generate_schemas();
}
fn generate_schemas() {
    let mut config = prost_build::Config::new();
    // Map the people protocol buffer namespace to the people::proto Rust module
    config.extern_path(".people", "people::proto");
    config
        .compile_protos(
            // Files we want to compile
            &["./schema/Team.proto"],
            // Folders where we can find our imports
            &["./schema"],
        )
        .expect("Prost protobuf compilation error;");
}

The function extern_path takes two arguments, the first one is the name of the namespace within the Protocol Buffers schema, and the second argument is the namespace within Rust.

The first argument of compile_protos is the files that we want to compile, the second argument is the directories in which we can find our schemas.

Note that we import People.proto in our new Protocol Buffers schema. Currently the only way that I know to get this to work, is to manually copy the .proto file in the other package into your own application.

There you have it! Next you can include the generated output into your Rust application. If you know of a better way to include external schemas without copying the schemas, please reach out.