From f8e8f107de0e668f47a7fe2b21a579a62a4ab57e Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Sun, 6 Oct 2024 12:51:37 -0400 Subject: [PATCH] New post --- content/blog/prost-import-external-schemas.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 content/blog/prost-import-external-schemas.md diff --git a/content/blog/prost-import-external-schemas.md b/content/blog/prost-import-external-schemas.md new file mode 100644 index 0000000..67dd5c8 --- /dev/null +++ b/content/blog/prost-import-external-schemas.md @@ -0,0 +1,78 @@ +--- +title: "Importing External Schemas in Prost" +date: 2024-10-05T11:19:37-07:00 +draft: false +tags: [] +math: false +medium_enabled: false +--- + +[Prost](https://github.com/tokio-rs/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: + +```protobuf +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 + +```rust +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` + +```protobuf +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. + +```rust +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.