Skip to main content

volo_build/
config_builder.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::Ok;
4use pilota_build::BoxClonePlugin;
5use volo::FastStr;
6
7use crate::{
8    model::{self, Entry},
9    util::{
10        DEFAULT_CONFIG_FILE, DEFAULT_DIR, ServiceBuilder, download_repos_to_target,
11        get_service_builders_from_services, open_config_file, read_config_from_file,
12    },
13};
14
15pub struct ConfigBuilder {
16    filename: PathBuf,
17    plugins: Vec<BoxClonePlugin>,
18}
19
20#[allow(clippy::large_enum_variant)]
21pub enum InnerBuilder {
22    Protobuf(
23        crate::Builder<crate::grpc_backend::MkGrpcBackend, pilota_build::parser::ProtobufParser>,
24    ),
25    Thrift(
26        crate::Builder<crate::thrift_backend::MkThriftBackend, pilota_build::parser::ThriftParser>,
27    ),
28}
29
30impl InnerBuilder {
31    fn thrift() -> Self {
32        InnerBuilder::Thrift(crate::Builder::thrift())
33    }
34
35    fn protobuf() -> Self {
36        InnerBuilder::Protobuf(crate::Builder::protobuf())
37    }
38
39    fn plugin<P: pilota_build::Plugin + 'static>(self, p: P) -> Self {
40        match self {
41            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.plugin(p)),
42            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.plugin(p)),
43        }
44    }
45
46    fn write(self) -> anyhow::Result<()> {
47        match self {
48            InnerBuilder::Protobuf(inner) => inner.write(),
49            InnerBuilder::Thrift(inner) => inner.write(),
50        }
51    }
52
53    fn init_service(self) -> anyhow::Result<(String, String)> {
54        match self {
55            InnerBuilder::Protobuf(inner) => inner.init_service(),
56            InnerBuilder::Thrift(inner) => inner.init_service(),
57        }
58    }
59
60    fn filename(self, filename: PathBuf) -> Self {
61        match self {
62            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.filename(filename)),
63            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.filename(filename)),
64        }
65    }
66
67    fn includes(self, includes: Vec<PathBuf>) -> Self {
68        match self {
69            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.include_dirs(includes)),
70            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.include_dirs(includes)),
71        }
72    }
73
74    pub fn add_service<P>(self, path: P) -> Self
75    where
76        P: AsRef<Path>,
77    {
78        match self {
79            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.add_service(path)),
80            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.add_service(path)),
81        }
82    }
83
84    pub fn add_services(mut self, service_builders: Vec<ServiceBuilder>) -> Self {
85        for ServiceBuilder {
86            path,
87            includes,
88            touch,
89            keep_unknown_fields,
90        } in service_builders
91        {
92            self = self
93                .add_service(path.clone())
94                .includes(includes)
95                .touch([(path.clone(), touch)]);
96            if keep_unknown_fields {
97                self = self.keep_unknown_fields([path])
98            }
99        }
100        self
101    }
102
103    pub fn touch(self, items: impl IntoIterator<Item = (PathBuf, Vec<impl Into<String>>)>) -> Self {
104        match self {
105            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.touch(items)),
106            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.touch(items)),
107        }
108    }
109
110    pub fn ignore_unused(self, ignore_unused: bool) -> Self {
111        match self {
112            InnerBuilder::Protobuf(inner) => {
113                InnerBuilder::Protobuf(inner.ignore_unused(ignore_unused))
114            }
115            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.ignore_unused(ignore_unused)),
116        }
117    }
118
119    pub fn keep_unknown_fields(self, keep: impl IntoIterator<Item = PathBuf>) -> Self {
120        match self {
121            InnerBuilder::Protobuf(inner) => {
122                InnerBuilder::Protobuf(inner.keep_unknown_fields(keep))
123            }
124            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.keep_unknown_fields(keep)),
125        }
126    }
127
128    pub fn split_generated_files(self, split_generated_files: bool) -> Self {
129        match self {
130            InnerBuilder::Protobuf(inner) => {
131                InnerBuilder::Protobuf(inner.split_generated_files(split_generated_files))
132            }
133            InnerBuilder::Thrift(inner) => {
134                InnerBuilder::Thrift(inner.split_generated_files(split_generated_files))
135            }
136        }
137    }
138
139    pub fn common_crate_name(self, name: FastStr) -> Self {
140        match self {
141            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.common_crate_name(name)),
142            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.common_crate_name(name)),
143        }
144    }
145
146    pub fn special_namings(self, namings: impl IntoIterator<Item = FastStr>) -> Self {
147        match self {
148            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.special_namings(namings)),
149            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.special_namings(namings)),
150        }
151    }
152
153    pub fn dedup(self, dedup_list: Vec<FastStr>) -> Self {
154        match self {
155            InnerBuilder::Protobuf(inner) => InnerBuilder::Protobuf(inner.dedup(dedup_list)),
156            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.dedup(dedup_list)),
157        }
158    }
159
160    pub fn with_descriptor(self, with_descriptor: bool) -> Self {
161        match self {
162            InnerBuilder::Protobuf(inner) => {
163                InnerBuilder::Protobuf(inner.with_descriptor(with_descriptor))
164            }
165            InnerBuilder::Thrift(inner) => {
166                InnerBuilder::Thrift(inner.with_descriptor(with_descriptor))
167            }
168        }
169    }
170
171    pub fn with_field_mask(self, with_field_mask: bool) -> Self {
172        match self {
173            InnerBuilder::Protobuf(inner) => {
174                InnerBuilder::Protobuf(inner.with_field_mask(with_field_mask))
175            }
176            InnerBuilder::Thrift(inner) => {
177                InnerBuilder::Thrift(inner.with_field_mask(with_field_mask))
178            }
179        }
180    }
181
182    pub fn with_comments(self, with_comments: bool) -> Self {
183        match self {
184            InnerBuilder::Protobuf(inner) => {
185                InnerBuilder::Protobuf(inner.with_comments(with_comments))
186            }
187            InnerBuilder::Thrift(inner) => InnerBuilder::Thrift(inner.with_comments(with_comments)),
188        }
189    }
190}
191
192impl ConfigBuilder {
193    pub fn new(filename: PathBuf) -> Self {
194        ConfigBuilder {
195            filename,
196            plugins: Vec::new(),
197        }
198    }
199
200    pub fn plugin<P: pilota_build::ClonePlugin + 'static>(mut self, p: P) -> Self {
201        self.plugins.push(BoxClonePlugin::new(p));
202
203        self
204    }
205
206    pub fn write(self) -> anyhow::Result<()> {
207        println!("cargo:rerun-if-changed={}", self.filename.display());
208        let mut f = open_config_file(self.filename.clone())?;
209        let config = read_config_from_file(&mut f)?;
210        config
211            .entries
212            .into_iter()
213            .try_for_each(|(entry_name, entry)| {
214                let mut builder = match entry.protocol {
215                    model::IdlProtocol::Thrift => InnerBuilder::thrift(),
216                    model::IdlProtocol::Protobuf => InnerBuilder::protobuf(),
217                }
218                .filename(entry.filename.clone());
219
220                for p in self.plugins.iter() {
221                    builder = builder.plugin(p.clone());
222                }
223
224                // download repos and get the repo paths
225                let target_dir = PathBuf::from(&*DEFAULT_DIR).join(entry_name);
226                let repo_dir_map = download_repos_to_target(&entry.repos, target_dir)?;
227
228                // get idl builders from services
229                let service_builders =
230                    get_service_builders_from_services(&entry.services, &repo_dir_map);
231
232                // add build options to the builder and build
233                builder
234                    .add_services(service_builders)
235                    .ignore_unused(!entry.common_option.touch_all)
236                    .special_namings(entry.common_option.special_namings)
237                    .split_generated_files(entry.common_option.split_generated_files)
238                    .dedup(entry.common_option.dedups)
239                    .with_descriptor(entry.common_option.with_descriptor)
240                    .with_field_mask(entry.common_option.with_field_mask)
241                    .with_comments(entry.common_option.with_comments)
242                    .write()?;
243
244                Ok(())
245            })?;
246        Ok(())
247    }
248}
249
250impl Default for ConfigBuilder {
251    fn default() -> Self {
252        ConfigBuilder::new(PathBuf::from(DEFAULT_CONFIG_FILE))
253    }
254}
255
256pub struct InitBuilder {
257    entry: Entry,
258}
259
260impl InitBuilder {
261    pub fn new(entry: Entry) -> Self {
262        InitBuilder { entry }
263    }
264
265    pub fn init(self) -> anyhow::Result<(String, String)> {
266        let mut builder = match self.entry.protocol {
267            model::IdlProtocol::Thrift => InnerBuilder::thrift(),
268            model::IdlProtocol::Protobuf => InnerBuilder::protobuf(),
269        }
270        .filename(self.entry.filename);
271
272        // download repos and get the repo paths
273        let temp_target_dir = tempfile::TempDir::new()?;
274        let repo_dir_map = download_repos_to_target(&self.entry.repos, temp_target_dir.as_ref())?;
275
276        // get idl builders from services
277        let idl_builders = get_service_builders_from_services(&self.entry.services, &repo_dir_map);
278
279        // add services to the builder
280        builder = builder.add_services(idl_builders);
281
282        builder.init_service()
283    }
284}