Skip to content

Commit

Permalink
configurable buf path (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
maksimryndin authored Jun 7, 2024
1 parent e21c285 commit b340ff0
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 25 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tonic-buf-build"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
description = "A build helper that integrates buf.build to tonic-build."
license = "MIT"
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,23 @@ fn main() -> Result<(), tonic_buf_build::error::TonicBufBuildError> {
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.

When the buf files are not located in the current directory, you can configure the *absolute* path to the directory, containing either `buf.yaml` or `buf.work.yaml`, and call the corresponding `tonic_buf_build::compile_from_buf_with_config` or `tonic_buf_build::compile_from_buf_workspace_with_config`.

Consider the following build.rs where the buf workspace directory is located one level above the crate (a usual case for multilanguage clients with common protos)

```rust
use std::env;
use std::path::PathBuf;

fn main() -> Result<(), tonic_buf_build::error::TonicBufBuildError> {
let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
path.pop();
tonic_buf_build::compile_from_buf_workspace_with_config(
tonic_build::configure(),
None,
tonic_buf_build::TonicBufConfig{buf_dir: Some(path)},
)?;
Ok(())
}
```
45 changes: 31 additions & 14 deletions src/buf.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::path::Path;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};

use serde::Deserialize;

Expand All @@ -10,12 +11,16 @@ pub(crate) struct BufYaml {
}

impl BufYaml {
pub(crate) fn load(file: &str) -> Result<BufYaml, TonicBufBuildError> {
let f = std::fs::File::open(file)
.map_err(|e| TonicBufBuildError::new(&format!("failed to read {}", file), e.into()))?;
pub(crate) fn load(file: &Path) -> Result<BufYaml, TonicBufBuildError> {
let f = std::fs::File::open(file).map_err(|e| {
TonicBufBuildError::new(&format!("failed to read {:?}", file.as_os_str()), e.into())
})?;

let buf: BufYaml = serde_yaml::from_reader(&f).map_err(|e| {
TonicBufBuildError::new(&format!("failed to deserialize {}", file), e.into())
TonicBufBuildError::new(
&format!("failed to deserialize {:?}", file.as_os_str()),
e.into(),
)
})?;
Ok(buf)
}
Expand All @@ -27,21 +32,25 @@ pub(crate) struct BufWorkYaml {
}

impl BufWorkYaml {
pub(crate) fn load(file: &str) -> Result<Self, TonicBufBuildError> {
let buf_work_file = std::fs::File::open(file)
.map_err(|e| TonicBufBuildError::new(&format!("failed to read {}", file), e.into()))?;
pub(crate) fn load(file: &Path) -> Result<Self, TonicBufBuildError> {
let buf_work_file = std::fs::File::open(file).map_err(|e| {
TonicBufBuildError::new(&format!("failed to read {:?}", file.as_os_str()), 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())
TonicBufBuildError::new(
&format!("failed to deserialize {:?}", file.as_os_str()),
e.into(),
)
})?;

Ok(buf_work)
}
}

pub(crate) fn ls_files() -> Result<Vec<String>, TonicBufBuildError> {
pub(crate) fn ls_files(proto_path: &Path) -> Result<Vec<String>, TonicBufBuildError> {
let child = std::process::Command::new("buf")
.args(["ls-files"])
.args([OsStr::new("ls-files"), proto_path.as_os_str()])
.output()
.map_err(|e| TonicBufBuildError::new("failed to execute `buf ls-files'", e.into()))?;

Expand Down Expand Up @@ -92,12 +101,20 @@ pub(crate) fn export_all(buf: &BufYaml, export_dir: &Path) -> Result<(), TonicBu
pub(crate) fn export_all_from_workspace(
buf_work: &BufWorkYaml,
export_dir: &Path,
) -> Result<(), TonicBufBuildError> {
workspace_dir: &Path,
) -> Result<Vec<PathBuf>, TonicBufBuildError> {
let mut buf_dirs = vec![];
if let Some(directories) = &buf_work.directories {
for dir in directories {
let buf = BufYaml::load(&format!("{}/buf.yaml", dir))?;
let mut buf_dir = PathBuf::from(workspace_dir);
buf_dir.push(dir);
buf_dirs.push(buf_dir.clone());
buf_dir.push("buf.yaml");

let buf = BufYaml::load(buf_dir.as_path())?;

export_all(&buf, export_dir)?;
}
}
Ok(())
Ok(buf_dirs)
}
58 changes: 48 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,60 @@
use error::TonicBufBuildError;
use scopeguard::defer;
use std::path::{Path, PathBuf};

mod buf;
pub mod error;

fn tempdir() -> std::path::PathBuf {
fn tempdir() -> PathBuf {
let mut temp_dir = std::env::temp_dir();
temp_dir.push(uuid::Uuid::new_v4().to_string());
temp_dir
}

#[derive(Default)]
pub struct TonicBufConfig<P: AsRef<Path> = &'static str> {
pub buf_dir: Option<P>,
}

pub fn compile_from_buf_workspace(
tonic_builder: tonic_build::Builder,
config: Option<prost_build::Config>,
) -> Result<(), TonicBufBuildError> {
compile_from_buf_workspace_with_config::<&'static str>(
tonic_builder,
config,
TonicBufConfig::default(),
)
}

pub fn compile_from_buf_workspace_with_config<P: AsRef<Path>>(
tonic_builder: tonic_build::Builder,
config: Option<prost_build::Config>,
tonic_buf_config: TonicBufConfig<P>,
) -> 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_dir = tonic_buf_config
.buf_dir
.as_ref()
.map(|p| p.as_ref())
.unwrap_or(".".as_ref());
let mut buf_work_file = PathBuf::from(buf_dir);
buf_work_file.push("buf.work.yaml");
let buf_work = buf::BufWorkYaml::load(buf_work_file.as_path())?;

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()];
let buf_work_directories = buf::export_all_from_workspace(&buf_work, &export_dir, buf_dir)?;
let mut includes = vec![export_dir.clone()];

for dep in buf_work_directories {
includes.push(dep);
}

let protos = buf::ls_files()?;
let protos = buf::ls_files(buf_dir)?;

match config {
None => tonic_builder.compile(&protos, &includes),
Expand All @@ -70,17 +93,32 @@ pub fn compile_from_buf_workspace(
pub fn compile_from_buf(
tonic_builder: tonic_build::Builder,
config: Option<prost_build::Config>,
) -> Result<(), TonicBufBuildError> {
compile_from_buf_with_config::<&'static str>(tonic_builder, config, TonicBufConfig::default())
}

pub fn compile_from_buf_with_config<P: AsRef<Path>>(
tonic_builder: tonic_build::Builder,
config: Option<prost_build::Config>,
tonic_buf_config: TonicBufConfig<P>,
) -> 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")?;
let buf_dir = tonic_buf_config
.buf_dir
.as_ref()
.map(|p| p.as_ref())
.unwrap_or(".".as_ref());
let mut buf_file = PathBuf::from(buf_dir);
buf_file.push("buf.yaml");
let buf = buf::BufYaml::load(buf_file.as_path())?;

buf::export_all(&buf, &export_dir)?;
let protos = buf::ls_files()?;
let includes = [".", export_dir.to_str().unwrap()];
let protos = buf::ls_files(buf_dir)?;
let includes = [buf_dir, &export_dir];

match config {
None => tonic_builder.compile(&protos, &includes),
Expand Down

0 comments on commit b340ff0

Please sign in to comment.