1use std::collections::HashSet;
8use std::fs;
9use std::fs::File;
10use std::io::Write;
11use std::path::PathBuf;
12
13use protobuf_codegen::Codegen;
14
15pub fn build_protos(out_dir: &PathBuf, proto_paths: &[PathBuf]) {
27 build_protos_explicit(
28 out_dir,
29 proto_paths,
30 proto_paths,
31 to_includes(proto_paths).as_slice(),
32 )
33}
34
35pub fn build_protos_explicit(
37 out_dir: &PathBuf,
38 proto_paths: &[PathBuf],
39 rebuild_if_changed_paths: &[PathBuf],
40 includes: &[PathBuf],
41) {
42 for file in rebuild_if_changed_paths {
43 println!(
45 "cargo:rerun-if-changed={}",
46 file.to_str().expect("proto path must be UTF-8")
47 );
48 }
49 fs::create_dir_all(out_dir).unwrap();
50 if !proto_paths.is_empty() {
51 gen_protos(out_dir, proto_paths, includes);
52 }
53 create_gen_file(out_dir, proto_paths);
54}
55
56fn to_includes(proto_paths: &[PathBuf]) -> Vec<PathBuf> {
59 let mut include_paths = HashSet::new();
60
61 for proto in proto_paths {
62 include_paths.insert(
63 proto
64 .parent()
65 .expect("protos must be files in a directory")
66 .to_owned(),
67 );
68 }
69
70 include_paths.drain().collect::<Vec<PathBuf>>()
71}
72
73fn gen_protos(out_dir: &PathBuf, proto_paths: &[PathBuf], includes: &[PathBuf]) {
74 Codegen::new()
75 .out_dir(out_dir)
76 .inputs(proto_paths)
77 .includes(includes)
78 .run()
79 .expect("failed to compile Rust protos");
80}
81
82fn create_gen_file(out_dir: &PathBuf, proto_files: &[PathBuf]) {
83 let generated = PathBuf::from(&out_dir).join("generated.rs");
84 let out = File::create(generated).expect("Failed to create generated file.");
85
86 for proto_path in proto_files {
87 let file_stem = proto_path.file_stem().unwrap().to_str().unwrap();
88 let out_dir = out_dir
89 .to_str()
90 .expect("path must be UTF-8")
91 .replace('\\', "/");
92 writeln!(&out, "#[path = \"{out_dir}/{file_stem}.rs\"]")
93 .expect("failed to write to generated.");
94 writeln!(&out, "pub mod {file_stem};").expect("failed to write to generated.");
95 }
96}