1use crate::codegen;
2use std::io;
3use std::path::{Path, PathBuf};
4
5pub fn compile_protos(
13 protos: &[impl AsRef<Path>],
14 includes: &[impl AsRef<Path>],
15) -> io::Result<()> {
16 configure().compile_protos(protos, includes)
17}
18
19pub fn configure() -> Builder {
21 Builder {
22 file_descriptor_set_path: None,
23 }
24}
25
26pub struct Builder {
28 file_descriptor_set_path: Option<PathBuf>,
29}
30
31impl Builder {
32 pub fn file_descriptor_set_path(mut self, path: impl AsRef<Path>) -> Self {
35 self.file_descriptor_set_path = Some(path.as_ref().to_path_buf());
36 self
37 }
38
39 pub fn compile_protos(
41 self,
42 protos: &[impl AsRef<Path>],
43 includes: &[impl AsRef<Path>],
44 ) -> io::Result<()> {
45 let tonic = tonic_prost_build::configure()
46 .build_client(true)
47 .build_server(true)
48 .build_transport(false);
49
50 let mut config = prost_build::Config::new();
51 configure_protoc(&mut config)?;
52 if let Some(path) = &self.file_descriptor_set_path {
53 config.file_descriptor_set_path(path);
54 }
55 config.service_generator(Box::new(codegen::ServiceGenerator::new(tonic)));
56 let include_paths = include_paths(includes)?;
57 config.compile_protos(protos, &include_paths)
58 }
59}
60
61fn configure_protoc(config: &mut prost_build::Config) -> io::Result<()> {
62 let protoc = protoc_bin_vendored::protoc_bin_path().map_err(other_io_error)?;
63 config.protoc_executable(protoc);
64 Ok(())
65}
66
67fn include_paths(includes: &[impl AsRef<Path>]) -> io::Result<Vec<PathBuf>> {
68 let mut include_paths = includes
69 .iter()
70 .map(|path| path.as_ref().to_path_buf())
71 .collect::<Vec<_>>();
72 let vendored_include = protoc_bin_vendored::include_path().map_err(other_io_error)?;
73 if !include_paths.iter().any(|path| path == &vendored_include) {
74 include_paths.push(vendored_include);
75 }
76 Ok(include_paths)
77}
78
79fn other_io_error(error: impl std::fmt::Display) -> io::Error {
80 io::Error::other(error.to_string())
81}