use std::{collections::HashSet, env, fmt::Write, fs, path::Path};
use walkdir::WalkDir;
type Res = Result<(), Box<dyn std::error::Error>>;
fn main() -> Res {
let mut protos = vec![];
let mut pkgs = HashSet::new();
let proto_path = concat!(env!("CARGO_MANIFEST_DIR"), "/proto");
for entry in WalkDir::new(proto_path)
.into_iter()
.map(|e| e.unwrap())
.filter(|e| {
(
e.path().to_str().unwrap().contains("googleads/v18")
|| e.path().to_str().unwrap().contains("google/rpc")
|| e.path().to_str().unwrap().contains("google/longrunning")
) && e
.path()
.extension()
.map_or(false, |ext| ext.to_str().unwrap() == "proto")
})
{
let path = entry.path();
protos.push(path.to_owned());
let content = fs::read_to_string(path).unwrap();
let pkg = content
.lines()
.find(|line| line.starts_with("package "))
.unwrap()
.split("//")
.next()
.unwrap()
.trim()
.trim_start_matches("package ")
.trim_end_matches(';');
pkgs.insert(pkg.to_string());
}
tonic_build::configure()
.build_server(false)
.compile(&protos, &[Path::new(proto_path)])?;
write_protos_rs(pkgs)?;
println!("cargo:rerun-if-changed=proto");
Ok(())
}
fn write_protos_rs(pkgs: HashSet<String>) -> Res {
let protos_rs = &mut String::new();
let mut packages: Vec<String> = pkgs.into_iter().collect();
packages.sort();
let mut path_stack: Vec<String> = vec![];
for pkg in packages {
let pop_to = pkg
.split('.')
.map(map_keyword)
.enumerate()
.position(|(idx, pkg_seg)| {
path_stack
.get(idx)
.map_or(true, |stack_seg| stack_seg != &pkg_seg)
})
.unwrap_or(0);
while path_stack.len() > pop_to {
path_stack.pop();
writeln!(protos_rs, "}}")?;
}
for seg in pkg.split('.').skip(pop_to).map(map_keyword) {
writeln!(protos_rs, "pub mod {} {{", &seg)?;
path_stack.push(seg);
}
writeln!(
protos_rs,
"tonic::include_proto!(\"{}\");",
path_stack.join(".")
)?;
}
while !path_stack.is_empty() {
path_stack.pop();
writeln!(protos_rs, "}}").unwrap();
}
fs::write(format!("{}/protos.rs", env::var("OUT_DIR")?), protos_rs)?;
Ok(())
}
fn map_keyword(kw: &str) -> String {
let mut ident = kw.to_string();
match ident.as_str() {
"as" | "break" | "const" | "continue" | "else" | "enum" | "false"
| "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut"
| "pub" | "ref" | "return" | "static" | "struct" | "trait" | "true"
| "type" | "unsafe" | "use" | "where" | "while"
| "dyn"
| "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" | "priv" | "typeof"
| "unsized" | "virtual" | "yield"
| "async" | "await" | "try" => ident.insert_str(0, "r#"),
"self" | "super" | "extern" | "crate" => ident += "_",
_ => (),
}
ident
}