From 89da80ac7d6e602951ba82f6f97d60fa205319f3 Mon Sep 17 00:00:00 2001 From: Moray Baruh Date: Mon, 14 Aug 2023 22:05:39 +0300 Subject: [PATCH] initial implementation --- .github/workflows/check.yaml | 26 +++++ .github/workflows/release.yaml | 40 ++++++++ .gitignore | 72 ++++++++++++++ Cargo.toml | 17 ++++ LICENSE.md | 21 ++++ README.md | 27 ++++- examples/buf-work-yaml/Cargo.toml | 15 +++ examples/buf-work-yaml/buf-module/buf.lock | 8 ++ examples/buf-work-yaml/buf-module/buf.yaml | 9 ++ .../buf-work-yaml/buf-module/sample.proto | 15 +++ examples/buf-work-yaml/buf.work.yaml | 3 + examples/buf-work-yaml/build.rs | 4 + examples/buf-work-yaml/src/main.rs | 31 ++++++ examples/buf-yaml/Cargo.toml | 13 +++ examples/buf-yaml/buf.lock | 8 ++ examples/buf-yaml/buf.yaml | 9 ++ examples/buf-yaml/build.rs | 4 + examples/buf-yaml/sample.proto | 15 +++ examples/buf-yaml/src/main.rs | 31 ++++++ src/buf.rs | 98 +++++++++++++++++++ src/error.rs | 34 +++++++ src/lib.rs | 88 +++++++++++++++++ 22 files changed, 587 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/check.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE.md create mode 100644 examples/buf-work-yaml/Cargo.toml create mode 100644 examples/buf-work-yaml/buf-module/buf.lock create mode 100644 examples/buf-work-yaml/buf-module/buf.yaml create mode 100644 examples/buf-work-yaml/buf-module/sample.proto create mode 100644 examples/buf-work-yaml/buf.work.yaml create mode 100644 examples/buf-work-yaml/build.rs create mode 100644 examples/buf-work-yaml/src/main.rs create mode 100644 examples/buf-yaml/Cargo.toml create mode 100644 examples/buf-yaml/buf.lock create mode 100644 examples/buf-yaml/buf.yaml create mode 100644 examples/buf-yaml/build.rs create mode 100644 examples/buf-yaml/sample.proto create mode 100644 examples/buf-yaml/src/main.rs create mode 100644 src/buf.rs create mode 100644 src/error.rs create mode 100644 src/lib.rs diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml new file mode 100644 index 0000000..6415ad4 --- /dev/null +++ b/.github/workflows/check.yaml @@ -0,0 +1,26 @@ +name: Check + +on: + push: + branches: + - "master" + pull_request: + branches: + - "master" + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install clippy + run: rustup component add clippy + - name: Run clippy + run: cargo clippy --verbose + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..cd8b49c --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,40 @@ +name: Release +on: + push: + tags: + - '*' + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install clippy + run: rustup component add clippy + - name: Run clippy + run: cargo clippy --verbose + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build + + publish: + needs: [lint, build, test] + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - name: Publish to crates.io + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish --token ${CARGO_REGISTRY_TOKEN} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..694c96b --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..696ac5a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tonic-buf-build" +version = "0.1.0" +edition = "2021" +description = "A build helper that integrates buf.build to tonic-build." +license = "MIT" +repository = "https://github.com/Valensas/tonic-buf-build" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tonic-build = "0.9.2" +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.9.22" +uuid = { version = "1.2.2", features = ["v4", "fast-rng"] } +prost-build = "*" +scopeguard = "1.2.0" diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..1103a33 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Valensas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b461e6f..1327be2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ # tonic-buf-build -A build helper that integrates buf.build to tonic-build + +A build helper that allows you to integrate [buf.build](https://buf.build) with [tonic-build](https://github.com/hyperium/tonic/tree/master/tonic-build). +Using buf.build and tonic, you can easily manage third party dependencies for proto files and generate code for your proto files in Rust. +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). + +## Usage + +Add the following to your Cargo.toml: + +```toml +tonic_buf_build = {path = "../../"} +tonic-build = "*" +``` + +Then, in your build.rs: + +```rust +fn main() -> Result<(), tonic_buf_build::error::TonicBufBuildError> { + tonic_buf_build::compile_from_buf(tonic_build::configure(), None)?; + Ok(()) +} +``` + +To use buf workspaces, simply call `tonic_buf_build::compile_from_buf_workspace` instead. + +For complete and working examples, take a look at the examples folder. diff --git a/examples/buf-work-yaml/Cargo.toml b/examples/buf-work-yaml/Cargo.toml new file mode 100644 index 0000000..b3bc846 --- /dev/null +++ b/examples/buf-work-yaml/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "buf-work-yaml" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tonic = {version = "0.9.2", features = ["tls"]} +tokio = {version = "1.28.2", features = ["full"]} +prost = "0.11.9" + +[build-dependencies] +tonic_buf_build = {path = "../../"} +tonic-build = "*" diff --git a/examples/buf-work-yaml/buf-module/buf.lock b/examples/buf-work-yaml/buf-module/buf.lock new file mode 100644 index 0000000..3435191 --- /dev/null +++ b/examples/buf-work-yaml/buf-module/buf.lock @@ -0,0 +1,8 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 711e289f6a384c4caeebaff7c6931ade + digest: shake256:e08fb55dad7469f69df00304eed31427d2d1576e9aab31e6bf86642688e04caaf0372f15fe6974cf79432779a635b3ea401ca69c943976dc42749524e4c25d94 diff --git a/examples/buf-work-yaml/buf-module/buf.yaml b/examples/buf-work-yaml/buf-module/buf.yaml new file mode 100644 index 0000000..5708304 --- /dev/null +++ b/examples/buf-work-yaml/buf-module/buf.yaml @@ -0,0 +1,9 @@ +version: v1 +deps: + - buf.build/googleapis/googleapis +lint: + except: + - PACKAGE_VERSION_SUFFIX + - PACKAGE_DIRECTORY_MATCH + rpc_allow_google_protobuf_empty_requests: true + rpc_allow_google_protobuf_empty_responses: true diff --git a/examples/buf-work-yaml/buf-module/sample.proto b/examples/buf-work-yaml/buf-module/sample.proto new file mode 100644 index 0000000..7b912e1 --- /dev/null +++ b/examples/buf-work-yaml/buf-module/sample.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package tonic_buf_build_sample; + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; + +service HelloService { + rpc SayHello(google.protobuf.Empty) returns (SayHelloResponse) { + option (google.api.http) = {get: "/hello"}; + } +} + +message SayHelloResponse { + string value = 1; +} diff --git a/examples/buf-work-yaml/buf.work.yaml b/examples/buf-work-yaml/buf.work.yaml new file mode 100644 index 0000000..5be12dc --- /dev/null +++ b/examples/buf-work-yaml/buf.work.yaml @@ -0,0 +1,3 @@ +version: v1 +directories: + - buf-module \ No newline at end of file diff --git a/examples/buf-work-yaml/build.rs b/examples/buf-work-yaml/build.rs new file mode 100644 index 0000000..554bf9b --- /dev/null +++ b/examples/buf-work-yaml/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), tonic_buf_build::error::TonicBufBuildError> { + tonic_buf_build::compile_from_buf_workspace(tonic_build::configure(), None)?; + Ok(()) +} diff --git a/examples/buf-work-yaml/src/main.rs b/examples/buf-work-yaml/src/main.rs new file mode 100644 index 0000000..47f4c9c --- /dev/null +++ b/examples/buf-work-yaml/src/main.rs @@ -0,0 +1,31 @@ +use std::result::Result; + +use proto::{hello_service_server::HelloServiceServer, SayHelloResponse}; +use tonic::{transport::Server, Request, Response, Status}; + +pub mod proto { + tonic::include_proto!("tonic_buf_build_sample"); +} + +#[derive(Clone)] +struct HelloServer {} + +#[tonic::async_trait] +impl proto::hello_service_server::HelloService for HelloServer { + async fn say_hello(&self, _: Request<()>) -> Result, Status> { + Ok(Response::new(SayHelloResponse { + value: "Hello world!".into(), + })) + } +} + +#[tokio::main] +async fn main() { + let hello_server = HelloServer {}; + + Server::builder() + .add_service(HelloServiceServer::new(hello_server)) + .serve("127.0.0.1:10000".parse().unwrap()) + .await + .unwrap(); +} diff --git a/examples/buf-yaml/Cargo.toml b/examples/buf-yaml/Cargo.toml new file mode 100644 index 0000000..6fecb83 --- /dev/null +++ b/examples/buf-yaml/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "buf-yaml" +version = "0.1.0" +edition = "2021" + +[dependencies] +tonic = {version = "0.9.2", features = ["tls"]} +tokio = {version = "1.28.2", features = ["full"]} +prost = "0.11.9" + +[build-dependencies] +tonic_buf_build = {path = "../../"} +tonic-build = "*" diff --git a/examples/buf-yaml/buf.lock b/examples/buf-yaml/buf.lock new file mode 100644 index 0000000..3435191 --- /dev/null +++ b/examples/buf-yaml/buf.lock @@ -0,0 +1,8 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 711e289f6a384c4caeebaff7c6931ade + digest: shake256:e08fb55dad7469f69df00304eed31427d2d1576e9aab31e6bf86642688e04caaf0372f15fe6974cf79432779a635b3ea401ca69c943976dc42749524e4c25d94 diff --git a/examples/buf-yaml/buf.yaml b/examples/buf-yaml/buf.yaml new file mode 100644 index 0000000..5708304 --- /dev/null +++ b/examples/buf-yaml/buf.yaml @@ -0,0 +1,9 @@ +version: v1 +deps: + - buf.build/googleapis/googleapis +lint: + except: + - PACKAGE_VERSION_SUFFIX + - PACKAGE_DIRECTORY_MATCH + rpc_allow_google_protobuf_empty_requests: true + rpc_allow_google_protobuf_empty_responses: true diff --git a/examples/buf-yaml/build.rs b/examples/buf-yaml/build.rs new file mode 100644 index 0000000..f0af40c --- /dev/null +++ b/examples/buf-yaml/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), tonic_buf_build::error::TonicBufBuildError> { + tonic_buf_build::compile_from_buf(tonic_build::configure(), None)?; + Ok(()) +} diff --git a/examples/buf-yaml/sample.proto b/examples/buf-yaml/sample.proto new file mode 100644 index 0000000..7b912e1 --- /dev/null +++ b/examples/buf-yaml/sample.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package tonic_buf_build_sample; + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; + +service HelloService { + rpc SayHello(google.protobuf.Empty) returns (SayHelloResponse) { + option (google.api.http) = {get: "/hello"}; + } +} + +message SayHelloResponse { + string value = 1; +} diff --git a/examples/buf-yaml/src/main.rs b/examples/buf-yaml/src/main.rs new file mode 100644 index 0000000..47f4c9c --- /dev/null +++ b/examples/buf-yaml/src/main.rs @@ -0,0 +1,31 @@ +use std::result::Result; + +use proto::{hello_service_server::HelloServiceServer, SayHelloResponse}; +use tonic::{transport::Server, Request, Response, Status}; + +pub mod proto { + tonic::include_proto!("tonic_buf_build_sample"); +} + +#[derive(Clone)] +struct HelloServer {} + +#[tonic::async_trait] +impl proto::hello_service_server::HelloService for HelloServer { + async fn say_hello(&self, _: Request<()>) -> Result, Status> { + Ok(Response::new(SayHelloResponse { + value: "Hello world!".into(), + })) + } +} + +#[tokio::main] +async fn main() { + let hello_server = HelloServer {}; + + Server::builder() + .add_service(HelloServiceServer::new(hello_server)) + .serve("127.0.0.1:10000".parse().unwrap()) + .await + .unwrap(); +} diff --git a/src/buf.rs b/src/buf.rs new file mode 100644 index 0000000..a380595 --- /dev/null +++ b/src/buf.rs @@ -0,0 +1,98 @@ +use std::path::Path; + +use serde::Deserialize; + +use crate::error::TonicBufBuildError; + +#[derive(Debug, PartialEq, Deserialize)] +pub(crate) struct BufYaml { + pub deps: Option>, +} + +impl BufYaml { + pub(crate) fn load(file: &str) -> Result { + let f = std::fs::File::open(file) + .map_err(|e| TonicBufBuildError::new(&format!("failed to read {}", file), e.into()))?; + + let buf: BufYaml = serde_yaml::from_reader(&f) + .map_err(|e| TonicBufBuildError::new(&format!("failed to deserialize {}", file), e.into()))?; + Ok(buf) + } +} + +#[derive(Debug, PartialEq, Deserialize)] +pub(crate) struct BufWorkYaml { + pub directories: Option>, +} + +impl BufWorkYaml { + pub(crate) fn load(file: &str) -> Result { + let buf_work_file = std::fs::File::open(file) + .map_err(|e| TonicBufBuildError::new(&format!("failed to read {}", file), e.into()))?; + + let buf_work: BufWorkYaml = serde_yaml::from_reader(&buf_work_file) + .map_err(|e| TonicBufBuildError::new(&format!("failed to deserialize {}", file), e.into()))?; + + Ok(buf_work) + } +} + +pub(crate) fn ls_files() -> Result, TonicBufBuildError> { + let child = std::process::Command::new("buf") + .args(["ls-files"]) + .output() + .map_err(|e| TonicBufBuildError::new("failed to execute `buf ls-files'", e.into()))?; + + if !child.status.success() { + return Err(TonicBufBuildError::new_without_cause(&format!( + "failed to execute `buf ls-files', returned status code {}: {}", + child.status.code().unwrap_or(-1), + std::str::from_utf8(&child.stderr).unwrap() + ))); + } + let protos = std::str::from_utf8(&child.stdout) + .map_err(|e| TonicBufBuildError::new("failed to decode `buf ls-files' output", e.into()))? + .trim_end() + .split('\n') + .map(|s| s.to_string()) + .collect::>(); + + Ok(protos) +} + +pub(crate) fn export_all(buf: &BufYaml, export_dir: &Path) -> Result<(), TonicBufBuildError> { + let export_dir = export_dir.to_str().unwrap(); + + if let Some(deps) = &buf.deps { + for dep in deps { + std::process::Command::new("buf") + .args(["export", dep, "-o", export_dir]) + .spawn() + .map_err(|e| { + TonicBufBuildError::new( + &format!("failed to execute `buf export {} -o {}'", &dep, &export_dir), + e.into(), + ) + })? + .wait() + .map_err(|e| { + TonicBufBuildError::new( + &format!("failed to execute `buf export {} -o {}'", &dep, &export_dir), + e.into(), + ) + })?; + } + } + + Ok(()) +} + +pub(crate) fn export_all_from_workspace(buf_work: &BufWorkYaml, export_dir: &Path) -> Result<(), TonicBufBuildError> { + if let Some(directories) = &buf_work.directories { + for dir in directories { + let buf = BufYaml::load(&format!("{}/buf.yaml", dir))?; + export_all(&buf, export_dir)?; + } + } + Ok(()) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..a84c433 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,34 @@ +use std::{error::Error, fmt::Display}; + +#[derive(Debug)] +pub struct TonicBufBuildError { + pub message: String, + pub cause: Option>, +} + +impl TonicBufBuildError { + pub(crate) fn new(message: &str, cause: Box) -> Self { + Self { + message: message.into(), + cause: Some(cause), + } + } + + pub(crate) fn new_without_cause(message: &str) -> Self { + Self { + message: message.into(), + cause: None, + } + } +} + +impl Display for TonicBufBuildError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.cause { + Some(cause) => f.write_str(&format!("{}: {}", self.message, cause)), + None => f.write_str(&self.message), + } + } +} + +impl Error for TonicBufBuildError {} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..232c558 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,88 @@ +//! tonic_buf_build allows you to integrate [buf.build](https://buf.build) with [tonic-build](https://github.com/hyperium/tonic/tree/master/tonic-build). +//! Using buf.build and tonic, you can easily manage third party dependencies for proto files and generate code for your proto files in Rust. +//! 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). +//! +//! +//! ## Usage +//! +//! Add the following to your Cargo.toml: +//! +//! ```toml +//! tonic_buf_build = {path = "../../"} +//! tonic-build = "*" +//! ``` +//! +//! Then, in your build.rs: +//! +//! ```rust +//! fn main() -> Result<(), tonic_buf_build::error::TonicBufBuildError> { +//! tonic_buf_build::compile_from_buf(tonic_build::configure(), None)?; +//! Ok(()) +//! } +//! ``` +//! +//! To use buf workspaces, you simply call `tonic_buf_build::compile_from_buf_workspace` instead. +//! +//! For complete and working examples, take a look at the examples folder. +//! + +use error::TonicBufBuildError; +use scopeguard::defer; + +mod buf; +pub mod error; + +fn tempdir() -> std::path::PathBuf { + let mut temp_dir = std::env::temp_dir(); + temp_dir.push(uuid::Uuid::new_v4().to_string()); + temp_dir +} + +pub fn compile_from_buf_workspace( + tonic_builder: tonic_build::Builder, config: Option, +) -> Result<(), TonicBufBuildError> { + let export_dir = tempdir(); + defer! { + // This is just cleanup, it's not important if it fails + let _ = std::fs::remove_dir(&export_dir); + } + + let buf_work = buf::BufWorkYaml::load("buf.work.yaml")?; + + buf::export_all_from_workspace(&buf_work, &export_dir)?; + let buf_work_directories = buf_work.directories.unwrap_or_default(); + let mut includes = vec![export_dir.to_str().unwrap().to_string()]; + + for dep in buf_work_directories { + includes.push(dep); + } + + let protos = buf::ls_files()?; + + match config { + None => tonic_builder.compile(&protos, &includes), + Some(config) => tonic_builder.compile_with_config(config, &protos, &includes), + } + .map_err(|e| TonicBufBuildError::new("error running tonic build", e.into())) +} + +pub fn compile_from_buf( + tonic_builder: tonic_build::Builder, config: Option, +) -> Result<(), TonicBufBuildError> { + let export_dir = tempdir(); + defer! { + // This is just cleanup, it's not important if it fails + let _ = std::fs::remove_dir(&export_dir); + } + let buf = buf::BufYaml::load("buf.yaml")?; + + buf::export_all(&buf, &export_dir)?; + let protos = buf::ls_files()?; + let includes = [".", export_dir.to_str().unwrap()]; + + match config { + None => tonic_builder.compile(&protos, &includes), + Some(config) => tonic_builder.compile_with_config(config, &protos, &includes), + } + .map_err(|e| TonicBufBuildError::new("error running tonic build", e.into())) +}