Skip to main content

tonic_buf_build/
lib.rs

1//! tonic-buf-build allows you to integrate [buf.build](https://buf.build) with [tonic-build](https://github.com/hyperium/tonic/tree/master/tonic-build).
2//! Using buf.build and tonic, you can easily manage third party dependencies for proto files and generate code for your proto files in Rust.
3//! Works with both [buf.yaml](https://buf.build/docs/configuration/v1/buf-yaml) and [buf.work.yaml](https://buf.build/docs/configuration/v1/buf-work-yaml).
4//!
5//!
6//! ## Usage
7//!
8//! Add the following to your Cargo.toml:
9//!
10//! ```toml
11//! tonic-buf-build = "*"
12//! tonic-build = "*"
13//! ```
14//!
15//! Then, in your build.rs:
16//!
17//! ```rust
18//! fn main() -> Result<(), tonic_buf_build::error::TonicBufBuildError> {
19//!    tonic_buf_build::compile_from_buf(tonic_build::configure(), None)?;
20//!    Ok(())
21//! }
22//! ```
23//!
24//! To use buf workspaces, you simply call `tonic-buf-build::compile_from_buf_workspace` instead.
25//!
26//! For complete and working examples, take a look at the examples folder.
27//!
28
29use 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        // This is just cleanup, it's not important if it fails
66        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        // This is just cleanup, it's not important if it fails
112        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}