1use error::TonicBufBuildError;
30use scopeguard::defer;
31use std::path::{Path, PathBuf};
32
33mod buf;
34pub mod error;
35
36fn tempdir() -> PathBuf {
37 let mut temp_dir = std::env::temp_dir();
38 temp_dir.push(uuid::Uuid::new_v4().to_string());
39 temp_dir
40}
41
42#[derive(Default)]
43pub struct TonicBufConfig<P: AsRef<Path> = &'static str> {
44 pub buf_dir: Option<P>,
45}
46
47pub fn compile_from_buf_workspace(
48 tonic_builder: tonic_build::Builder,
49 config: Option<tonic_build::Config>,
50) -> Result<(), TonicBufBuildError> {
51 compile_from_buf_workspace_with_config::<&'static str>(
52 tonic_builder,
53 config,
54 TonicBufConfig::default(),
55 )
56}
57
58pub fn compile_from_buf_workspace_with_config<P: AsRef<Path>>(
59 tonic_builder: tonic_build::Builder,
60 config: Option<tonic_build::Config>,
61 tonic_buf_config: TonicBufConfig<P>,
62) -> Result<(), TonicBufBuildError> {
63 let export_dir = tempdir();
64 defer! {
65 let _ = std::fs::remove_dir(&export_dir);
67 }
68 let buf_dir = tonic_buf_config
69 .buf_dir
70 .as_ref()
71 .map(|p| p.as_ref())
72 .unwrap_or(".".as_ref());
73 let mut buf_work_file = PathBuf::from(buf_dir);
74 buf_work_file.push("buf.work.yaml");
75 let buf_work = buf::BufWorkYaml::load(buf_work_file.as_path())?;
76
77 let buf_work_directories = buf::export_all_from_workspace(&buf_work, &export_dir, buf_dir)?;
78 let mut includes = vec![export_dir.clone()];
79
80 for dep in buf_work_directories {
81 includes.push(dep);
82 }
83
84 let protos = includes
85 .iter()
86 .filter_map(|exp_dir| buf::ls_files(&exp_dir).ok())
87 .flatten()
88 .collect::<Vec<_>>();
89
90 match config {
91 None => tonic_builder.compile_protos(&protos, &includes),
92 Some(config) => tonic_builder.compile_protos_with_config(config, &protos, &includes),
93 }
94 .map_err(|e| TonicBufBuildError::new("error running tonic build", e.into()))
95}
96
97pub fn compile_from_buf(
98 tonic_builder: tonic_build::Builder,
99 config: Option<tonic_build::Config>,
100) -> Result<(), TonicBufBuildError> {
101 compile_from_buf_with_config::<&'static str>(tonic_builder, config, TonicBufConfig::default())
102}
103
104pub fn compile_from_buf_with_config<P: AsRef<Path>>(
105 tonic_builder: tonic_build::Builder,
106 config: Option<tonic_build::Config>,
107 tonic_buf_config: TonicBufConfig<P>,
108) -> Result<(), TonicBufBuildError> {
109 let export_dir = tempdir();
110 defer! {
111 let _ = std::fs::remove_dir(&export_dir);
113 }
114 let buf_dir = tonic_buf_config
115 .buf_dir
116 .as_ref()
117 .map(|p| p.as_ref())
118 .unwrap_or(".".as_ref());
119 let mut buf_file = PathBuf::from(buf_dir);
120 buf_file.push("buf.yaml");
121 let buf = buf::BufYaml::load(buf_file.as_path())?;
122
123 buf::export_all(&buf, &export_dir)?;
124 let protos = buf::ls_files(&export_dir)?;
125 let includes = [buf_dir, &export_dir];
126
127 match config {
128 None => tonic_builder.compile_protos(&protos, &includes),
129 Some(config) => tonic_builder.compile_protos_with_config(config, &protos, &includes),
130 }
131 .map_err(|e| TonicBufBuildError::new("error running tonic build", e.into()))
132}