Skip to content

Commit

Permalink
prost-build: Extract file descriptor loading from compile_protos() (#…
Browse files Browse the repository at this point in the history
…1067)

* prost-build: Extract file descriptor loading from compile_protos()

* Update prost-build/src/config.rs

Co-authored-by: Casper Meijn <casper@meijn.net>

* Add concrete example for Config::load_fds()

---------

Co-authored-by: Casper Meijn <casper@meijn.net>
  • Loading branch information
swallez and caspermeijn authored Jul 19, 2024
1 parent e5cc951 commit 6009233
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 25 deletions.
85 changes: 63 additions & 22 deletions prost-build/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,37 +827,42 @@ impl Config {
Ok(())
}

/// Compile `.proto` files into Rust files during a Cargo build with additional code generator
/// configuration options.
///
/// This method is like the `prost_build::compile_protos` function, with the added ability to
/// specify non-default code generation options. See that function for more information about
/// the arguments and generated outputs.
///
/// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified.
/// Loads `.proto` files as a [`FileDescriptorSet`]. This allows inspection of the descriptors
/// before calling [`Config::compile_fds`]. This could be used to change [`Config`]
/// attributes after introspecting what is actually present in the `.proto` files.
///
/// # Example `build.rs`
///
/// ```rust,no_run
/// # use std::io::Result;
/// fn main() -> Result<()> {
/// let mut prost_build = prost_build::Config::new();
/// prost_build.btree_map(&["."]);
/// prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
/// Ok(())
/// # use prost_types::FileDescriptorSet;
/// # use prost_build::Config;
/// fn main() -> std::io::Result<()> {
/// let mut config = Config::new();
/// let file_descriptor_set = config.load_fds(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
///
/// // Add custom attributes to messages that are service inputs or outputs.
/// for file in &file_descriptor_set.file {
/// for service in &file.service {
/// for method in &service.method {
/// if let Some(input) = &method.input_type {
/// config.message_attribute(input, "#[derive(custom_proto::Input)]");
/// }
/// if let Some(output) = &method.output_type {
/// config.message_attribute(output, "#[derive(custom_proto::Output)]");
/// }
/// }
/// }
/// }
///
/// config.compile_fds(file_descriptor_set)
/// }
/// ```
pub fn compile_protos(

pub fn load_fds(
&mut self,
protos: &[impl AsRef<Path>],
includes: &[impl AsRef<Path>],
) -> Result<()> {
// TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
// according to [1] if any are output then those paths replace the default crate root,
// which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has
// this figured out.
// [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script

) -> Result<FileDescriptorSet> {
let tmp;
let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path {
path.clone()
Expand Down Expand Up @@ -944,6 +949,42 @@ impl Config {
)
})?;

Ok(file_descriptor_set)
}

/// Compile `.proto` files into Rust files during a Cargo build with additional code generator
/// configuration options.
///
/// This method is like the `prost_build::compile_protos` function, with the added ability to
/// specify non-default code generation options. See that function for more information about
/// the arguments and generated outputs.
///
/// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified.
///
/// # Example `build.rs`
///
/// ```rust,no_run
/// # use std::io::Result;
/// fn main() -> Result<()> {
/// let mut prost_build = prost_build::Config::new();
/// prost_build.btree_map(&["."]);
/// prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
/// Ok(())
/// }
/// ```
pub fn compile_protos(
&mut self,
protos: &[impl AsRef<Path>],
includes: &[impl AsRef<Path>],
) -> Result<()> {
// TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
// according to [1] if any are output then those paths replace the default crate root,
// which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has
// this figured out.
// [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script

let file_descriptor_set = self.load_fds(protos, includes)?;

self.compile_fds(file_descriptor_set)
}

Expand Down
2 changes: 2 additions & 0 deletions prost-build/src/fixtures/helloworld/_expected_helloworld.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// This file is @generated by prost-build.
#[derive(derive_builder::Builder)]
#[derive(custom_proto::Input)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Message {
#[prost(string, tag="1")]
pub say: ::prost::alloc::string::String,
}
#[derive(derive_builder::Builder)]
#[derive(custom_proto::Output)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Response {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// This file is @generated by prost-build.
#[derive(derive_builder::Builder)]
#[derive(custom_proto::Input)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Message {
#[prost(string, tag = "1")]
pub say: ::prost::alloc::string::String,
}
#[derive(derive_builder::Builder)]
#[derive(custom_proto::Output)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Response {
Expand Down
26 changes: 23 additions & 3 deletions prost-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,16 +392,36 @@ mod tests {
let _ = env_logger::try_init();
let tempdir = tempfile::tempdir().unwrap();

Config::new()
let mut config = Config::new();
config
.out_dir(tempdir.path())
// Add attributes to all messages and enums
.message_attribute(".", "#[derive(derive_builder::Builder)]")
.enum_attribute(".", "#[some_enum_attr(u8)]")
.compile_protos(
.enum_attribute(".", "#[some_enum_attr(u8)]");

let fds = config
.load_fds(
&["src/fixtures/helloworld/hello.proto"],
&["src/fixtures/helloworld"],
)
.unwrap();

// Add custom attributes to messages that are service inputs or outputs.
for file in &fds.file {
for service in &file.service {
for method in &service.method {
if let Some(input) = &method.input_type {
config.message_attribute(input, "#[derive(custom_proto::Input)]");
}
if let Some(output) = &method.output_type {
config.message_attribute(output, "#[derive(custom_proto::Output)]");
}
}
}
}

config.compile_fds(fds).unwrap();

let out_file = tempdir.path().join("helloworld.rs");
#[cfg(feature = "format")]
let expected_content =
Expand Down

0 comments on commit 6009233

Please sign in to comment.