use serde::{Deserialize, Serialize};
use std::{fs, process::Command};
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(default)]
pub struct BuildConfig {
pub includes: Vec<String>,
pub files: Vec<String>,
pub output: Option<String>,
pub opts: Vec<BuildOption>,
}
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(default)]
pub struct BuildOption {
pub scope: String,
pub description: String,
pub attr: String,
pub paths: Vec<String>,
}
pub fn build_with_serde(json: &str) -> BuildConfig {
let build_config: BuildConfig = serde_json::from_str(json).unwrap();
let output_dir: String = match &build_config.output {
None => {
let default_output_dir = std::env::var("OUT_DIR");
match default_output_dir {
Err(_) => String::new(),
Ok(cargo_out_dir) => cargo_out_dir,
}
}
Some(specified_output) => specified_output.to_owned(),
};
let mut config = prost_build::Config::new();
for opt in build_config.opts.iter() {
match opt.scope.as_ref() {
"bytes" => {
config.bytes(&opt.paths);
continue;
}
"btree_map" => {
config.btree_map(&opt.paths);
continue;
}
_ => (),
};
for path in opt.paths.iter() {
match opt.scope.as_str() {
"type" => config.type_attribute(path, opt.attr.as_str()),
"field" => config.field_attribute(path, opt.attr.as_str()),
v => panic!("Not supported type: {}", v),
};
}
}
fs::create_dir_all(&output_dir).unwrap();
config.out_dir(&output_dir);
config
.compile_protos(&build_config.files, &build_config.includes)
.unwrap_or_else(|e| panic!("Failed to compile proto files. Error: {:?}", e));
Command::new("cargo")
.args(&["fmt"])
.status()
.expect("cargo fmt failed");
build_config
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_serde_supported_code() {
let json = include_str!("../examples/build_config.json");
build_with_serde(json);
}
}