#![warn(missing_debug_implementations, missing_docs)]
#![doc(html_root_url = "https://docs.rs/prost-reflect-build/0.8.0/")]
use std::{
env, fs, io,
path::{Path, PathBuf},
};
use prost_reflect::DescriptorPool;
#[derive(Debug, Clone)]
pub struct Builder {
file_descriptor_set_path: PathBuf,
file_descriptor_expr: String,
}
impl Default for Builder {
fn default() -> Self {
let file_descriptor_set_path = env::var_os("OUT_DIR")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("."))
.join("file_descriptor_set.bin");
Self {
file_descriptor_set_path,
file_descriptor_expr: "crate::DESCRIPTOR_POOL".into(),
}
}
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
pub fn file_descriptor_set_path<P>(&mut self, path: P) -> &mut Self
where
P: Into<PathBuf>,
{
self.file_descriptor_set_path = path.into();
self
}
pub fn file_descriptor_expr<P>(&mut self, expr: P) -> &mut Self
where
P: Into<String>,
{
self.file_descriptor_expr = expr.into();
self
}
pub fn configure(
&mut self,
config: &mut prost_build::Config,
protos: &[impl AsRef<Path>],
includes: &[impl AsRef<Path>],
) -> io::Result<()> {
config
.file_descriptor_set_path(&self.file_descriptor_set_path)
.compile_protos(protos, includes)?;
let buf = fs::read(&self.file_descriptor_set_path)?;
let descriptor = DescriptorPool::decode(buf.as_ref()).expect("Invalid file descriptor");
for message in descriptor.all_messages() {
let full_name = message.full_name();
config
.type_attribute(full_name, "#[derive(::prost_reflect::ReflectMessage)]")
.type_attribute(
full_name,
&format!(
r#"#[prost_reflect(descriptor_pool = "{}", message_name = "{}")]"#,
self.file_descriptor_expr, full_name,
),
);
}
Ok(())
}
pub fn compile_protos_with_config(
&mut self,
mut config: prost_build::Config,
protos: &[impl AsRef<Path>],
includes: &[impl AsRef<Path>],
) -> io::Result<()> {
self.configure(&mut config, protos, includes)?;
config.skip_protoc_run().compile_protos(protos, includes)
}
pub fn compile_protos(
&mut self,
protos: &[impl AsRef<Path>],
includes: &[impl AsRef<Path>],
) -> io::Result<()> {
self.compile_protos_with_config(prost_build::Config::new(), protos, includes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config() {
let mut config = prost_build::Config::new();
let mut builder = Builder::new();
let tmpdir = std::env::temp_dir();
config.out_dir(tmpdir.clone());
builder
.file_descriptor_set_path(tmpdir.join("file_descriptor_set.bin"))
.compile_protos_with_config(config, &["src/test.proto"], &["src"])
.unwrap();
assert!(tmpdir.join("my.test.rs").exists());
let buf = fs::read_to_string(tmpdir.join("my.test.rs")).unwrap();
let num_derive = buf
.lines()
.filter(|line| line.trim_start() == "#[derive(::prost_reflect::ReflectMessage)]")
.count();
assert_eq!(num_derive, 3);
}
}