1use std::convert::AsRef;
2use std::iter::IntoIterator;
3use std::path::Path;
4
5use prost_build::Config;
6use prost_build::{Service, ServiceGenerator};
7use prost_types::FileDescriptorSet;
8
9use super::Preprocessor;
10
11pub struct Transpiler<'a> {
13 prost_config: Config,
14 files: FileDescriptorSet,
15 service_generator: MergedServiceGenerator,
16 preprocessors: Vec<Box<dyn Preprocessor + 'a>>,
17}
18
19impl<'a> Transpiler<'a> {
20 pub fn new(
21 files: impl IntoIterator<Item = impl AsRef<Path>>,
22 includes: impl IntoIterator<Item = impl AsRef<Path>>,
23 ) -> Result<Self, impl std::error::Error> {
24 let files: Vec<_> = files.into_iter().collect();
25 for f in &files {
26 println!("cargo:rerun-if-changed={}", f.as_ref().display());
27 }
28 Ok::<_, protox::Error>(Self {
29 prost_config: Config::new(),
30 files: protox::compile(files, includes)?,
31 service_generator: MergedServiceGenerator::empty(),
32 preprocessors: Vec::new(),
33 })
34 }
35
36 pub fn generate_all(mut self) -> Self {
38 self.service_generator
39 .add_service(super::ProtobufServiceGenerator::all(
40 std::env::var("OUT_DIR").unwrap().into(),
41 ));
42 self
43 }
44
45 pub fn generate_server(mut self) -> Self {
47 self.service_generator
48 .add_service(super::ProtobufServiceGenerator::server(
49 std::env::var("OUT_DIR").unwrap().into(),
50 ));
51 self
52 }
53
54 pub fn generate_client(mut self) -> Self {
56 self.service_generator
57 .add_service(super::ProtobufServiceGenerator::client(
58 std::env::var("OUT_DIR").unwrap().into(),
59 ));
60 self
61 }
62
63 pub fn with_service_generator<S: ServiceGenerator + 'static>(mut self, gen: S) -> Self {
65 self.service_generator.add_service(gen);
66 self
67 }
68
69 pub fn with_preprocessor<P: Preprocessor + 'a>(mut self, pp: P) -> Self {
71 self.preprocessors.push(Box::new(pp));
72 self
73 }
74
75 pub fn transpile(mut self) -> std::io::Result<()> {
77 let mut files = self.files;
78 let mut generated = String::new();
79 for mut pp in self.preprocessors {
80 pp.process(&mut files, &mut generated);
81 }
82 self.service_generator
83 .add_service(PreprocessedCodeGenInjector {
84 generated_str: generated,
85 });
86
87 self.prost_config
88 .service_generator(Box::new(self.service_generator))
89 .compile_fds(files)
90 }
91}
92
93struct PreprocessedCodeGenInjector {
94 generated_str: String,
95}
96
97impl ServiceGenerator for PreprocessedCodeGenInjector {
98 fn generate(&mut self, _: Service, buf: &mut String) {
99 buf.insert_str(0, &self.generated_str);
100 }
101}
102
103struct MergedServiceGenerator {
104 generators: Vec<Box<dyn ServiceGenerator + 'static>>,
105}
106
107impl MergedServiceGenerator {
108 fn empty() -> Self {
109 Self {
110 generators: Vec::new(),
111 }
112 }
113
114 fn add_service<S: ServiceGenerator + 'static>(&mut self, service: S) -> &mut Self {
115 self.generators.push(Box::new(service));
116 self
117 }
118}
119
120impl ServiceGenerator for MergedServiceGenerator {
121 fn generate(&mut self, service: Service, buf: &mut String) {
122 for gen in &mut self.generators {
123 gen.generate(service.clone(), buf);
124 }
125 }
126}
127
128pub fn compile(
130 files: impl IntoIterator<Item = impl AsRef<Path>>,
131 includes: impl IntoIterator<Item = impl AsRef<Path>>,
132) {
133 Transpiler::new(files, includes)
134 .unwrap()
135 .generate_all()
136 .transpile()
137 .unwrap();
138}
139
140pub fn compile_clients(
142 files: impl IntoIterator<Item = impl AsRef<Path>>,
143 includes: impl IntoIterator<Item = impl AsRef<Path>>,
144) {
145 Transpiler::new(files, includes)
146 .unwrap()
147 .generate_client()
148 .transpile()
149 .unwrap();
150}
151
152pub fn compile_servers(
154 files: impl IntoIterator<Item = impl AsRef<Path>>,
155 includes: impl IntoIterator<Item = impl AsRef<Path>>,
156) {
157 Transpiler::new(files, includes)
158 .unwrap()
159 .generate_server()
160 .transpile()
161 .unwrap();
162}